检测符号溢出的C / C ++ [英] Detecting signed overflow in C/C++

查看:224
本文介绍了检测符号溢出的C / C ++的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

乍一看,这个问题可能看起来像<一个副本href=\"http://stackoverflow.com/questions/199333/best-way-to-detect-integer-overflow-in-c-c\">http://stackoverflow.com/questions/199333/best-way-to-detect-integer-overflow-in-c-c,但它实际上是显著不同。

At first glance, this question may seem like a duplicate of http://stackoverflow.com/questions/199333/best-way-to-detect-integer-overflow-in-c-c, however it is actually significantly different.

我发现,在检测的无符号整数溢出是pretty琐碎,检测的签署的溢出,C / C ++其实比人们想象的更困难。

I've found that while detecting an unsigned integer overflow is pretty trivial, detecting a signed overflow in C/C++ is actually more difficult than most people think.

最明显的,但天真的,这样做会是这样的:

The most obvious, yet naive, way to do it would be something like:

int add(int lhs, int rhs)
{
 int sum = lhs + rhs;
 if ((lhs >= 0 && sum < rhs) || (lhs < 0 && sum > rhs)) {
  /* an overflow has occurred */
  abort();
 }
 return sum; 
}

这样做的问题是,根据C标准,有符号整数溢出的未定义的行为。的。换句话说,按照标准,只要你甚至引起符号溢出,你的程序只是为无效,如果你提领一空指针。所以你不能导致不确定的行为,然后再尝试检测溢出事实后,如上述条件后检查的例子。

The problem with this is that according to the C standard, signed integer overflow is undefined behavior. In other words, according to the standard, as soon as you even cause a signed overflow, your program is just as invalid as if you dereferenced a null pointer. So you can't cause undefined behavior, and then try to detect the overflow after the fact, as in the above post-condition check example.

尽管上面的检查很可能在许多编译器的工作,你不能指望它。事实上,因为C标准说符号的整数溢出是不确定的,一些编译器(如GCC)会优化掉上面的检查当优化标志设置,因为编译器假定一个符号溢出是不可能的。这完全打破了尝试检查溢出。

Even though the above check is likely to work on many compilers, you can't count on it. In fact, because the C standard says signed integer overflow is undefined, some compilers (like GCC) will optimize away the above check when optimization flags are set, because the compiler assumes a signed overflow is impossible. This totally breaks the attempt to check for overflow.

所以,另一种可能的方式来检查溢出将是:

So, another possible way to check for overflow would be:

int add(int lhs, int rhs)
{
 if (lhs >= 0 && rhs >= 0) {
  if (INT_MAX - lhs <= rhs) {
   /* overflow has occurred */
   abort();
  }
 }
 else if (lhs < 0 && rhs < 0) {
  if (lhs <= INT_MIN - rhs) {
   /* overflow has occurred */
   abort();
  }
 }

 return lhs + rhs;
}

这似乎更有前途,因为我们实际上并不添加两个整数在一起,直到我们提前确保执行这样一个附加不会导致溢出。因此,我们不会引起任何不确定的行为。

This seems more promising, since we don't actually add the two integers together until we make sure in advance that performing such an add will not result in overflow. Thus, we don't cause any undefined behavior.

但是,这个方案是不幸比最初的解决方案少了很多有效的,因为你必须执行减法操作只是为了测试,如果你的加法运算会工作。而且,即使你不关心这个(小)的性能损失,我还是不完全相信这个解决方案是足够的。这位前pression LHS&LT; = INT_MIN - 右轴看起来酷似排序前pression的编译器可能会优化掉,心想签署溢出是不可能的。

However, this solution is unfortunately a lot less efficient than the initial solution, since you have to perform a subtract operation just to test if your addition operation will work. And even if you don't care about this (small) performance hit, I'm still not entirely convinced this solution is adequate. The expression lhs <= INT_MIN - rhs seems exactly like the sort of expression the compiler might optimize away, thinking that signed overflow is impossible.

那么,有没有更好的解决办法吗?这是保证1东西)不会导致未定义的行为; 2)不能提供的编译器有机会优化掉溢出检查?我想可能有一些办法由铸造两个操作数为无符号,并通过滚动你自己的二进制补码算术执行检查做到这一点,但我真的不知道该怎么做。

So is there a better solution here? Something that is guaranteed to 1) not cause undefined behavior, and 2) not provide the compiler with an opportunity to optimize away overflow checks? I was thinking there might be some way to do it by casting both operands to unsigned, and performing checks by rolling your own two's-complement arithmetic, but I'm not really sure how to do that.

推荐答案

您用减法的方法是正确的和明确的。编译器不能优化它了。

Your approach with subtraction is correct and well-defined. A compiler cannot optimize it away.

另一种正确的做法,如果你有一个较大的整数类型,是在更大的类型进行运算,然后检查结果符合在较小的类型转换它,当回

Another correct approach, if you have a larger integer type available, is to perform the arithmetic in the larger type and then check that the result fits in the smaller type when converting it back

int sum(int a, int b)
{
    long long c;
    assert(LLONG_MAX>INT_MAX);
    c = (long long)a + b;
    if (c < INT_MIN || c > INT_MAX) abort();
    return c;
}

一个好的编译器应该转换整个加法和如果语句到 INT -sized加法和一个条件跳溢出时,从来没有真正执行较大的增加。

A good compiler should convert the entire addition and if statement into an int-sized addition and a single conditional jump-on-overflow and never actually perform the larger addition.

编辑:斯蒂芬指出的那样,我无法得到一个(不那么好)编译器gcc,生成健全的ASM。在code它会产生是不是非常慢,但肯定不理想。如果有人知道这个code,将让GCC做正确的事的变种,我很乐意看到他们。

As Stephen pointed out, I'm having trouble getting a (not-so-good) compiler, gcc, to generate the sane asm. The code it generates is not terribly slow, but certainly suboptimal. If anyone knows variants on this code that will get gcc to do the right thing, I'd love to see them.

这篇关于检测符号溢出的C / C ++的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆