乍一看,这个问题可能看起来像是How to detect integer overflow?的翻版,但实际上有很大不同.
我发现,虽然检测无符号整数溢出相当简单,但在C/C++中检测signed溢出实际上比大多数人想象的要困难得多.
最明显但最天真的方法是:
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标准,有符号整数溢出是undefined behavior.,换句话说,根据该标准,只要你引起一个有符号溢出,你的程序就像你取消引用一个空指针一样无效.因此,您不能导致未定义的行为,然后try 在事后检测溢出,如上面的后置条件判断示例所示.
尽管上面的判断可能会在许多编译器上运行,但您不能指望它.事实上,因为C标准说带符号的整数溢出是未定义的,所以一些编译器(如GCC)在设置优化标志时会出现optimize away the above check%,因为编译器认为带符号的溢出是不可能的.这完全打破了判断溢出的try .
因此,判断溢出的另一种可能方法是:
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;
}
这似乎更有希望,因为在我们事先确保执行这样的加法不会导致溢出之前,我们实际上不会将两个整数相加.因此,我们不会导致任何未定义的行为.
然而,不幸的是,此解决方案的效率远低于最初的解决方案,因为您必须执行减法运算才能测试加法运算是否有效.即使您不关心这种(小的)性能影响,我仍然不完全相信这个解决方案是足够的.表达式lhs <= INT_MIN - rhs
看起来完全像编译器可能优化掉的那种表达式,认为带符号的溢出是不可能的.
那么这里有更好的解决方案吗?保证1)不会导致未定义的行为,2)不会为编译器提供优化溢出判断的机会?我在想可能有办法做到这一点,将两个操作数都强制转换为无符号,并通过滚动自己的二补运算来执行判断,但我真的不确定如何做到这一点.