检测C / C ++中的带符号溢出乍一看,这个问题看起来像是如何检测整数溢出的重复?然而,它实际上是显着不同的。我发现虽然检测无符号整数溢出非常简单,但在C / C ++中检测带符号的溢出实际上比大多数人想象的要困难。最明显但又天真的方式是这样的: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标准,有符号整数溢出是未定义的行为。 换句话说,根据标准,只要您甚至导致签名溢出,您的程序就像取消引用空指针一样无效。因此,您不能导致未定义的行为,然后尝试在事后检测溢出,如上面的后置条件检查示例。尽管上面的检查很可能适用于许多编译器,但你不能指望它。实际上,因为C标准说未定义有符号整数溢出,所以一些编译器(如GCC)将在设置优化标志时优化上述检查,因为编译器假定有符号溢出是不可能的。这完全打破了检查溢出的尝试。因此,检查溢出的另一种可能方法是: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)不为编译器提供优化溢出检查的机会?我想可能有一些方法可以通过将两个操作数转换为无符号来执行它,并通过滚动自己的二进制补码算法来执行检查,但我不确定如何做到这一点。
3 回答
蝴蝶刀刀
TA贡献1801条经验 获得超8个赞
不,你的第二个代码不正确,但你很接近:如果你设置了
int half = INT_MAX/2;int half1 = half + 1;
添加的结果是INT_MAX
。(INT_MAX
总是一个奇数)。所以这是有效的输入。但是在你的日常生活中INT_MAX - half == half1
,你将会中止。误报。
可以通过放入<
而不是<=
在两个检查中修复此错误。
但是那时你的代码也不是最优的。以下是:
int add(int lhs, int rhs){ if (lhs >= 0) { if (INT_MAX - lhs < rhs) { /* would overflow */ abort(); } } else { if (rhs < INT_MIN - lhs) { /* would overflow */ abort(); } } return lhs + rhs;}
要看到这是有效的,你必须象征性地lhs
在不等式的两边添加,这给出了你的结果超出范围的算术条件。
- 3 回答
- 0 关注
- 610 浏览
添加回答
举报
0/150
提交
取消