猿问

检测C / C ++中的带符号溢出

检测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)不为编译器提供优化溢出检查的机会?我想可能有一些方法可以通过将两个操作数转换为无符号来执行它,并通过滚动自己的二进制补码算法来执行检查,但我不确定如何做到这一点。


MM们
浏览 613回答 3
3回答

蝴蝶刀刀

不,你的第二个代码不正确,但你很接近:如果你设置了int&nbsp;half&nbsp;=&nbsp;INT_MAX/2;int&nbsp;half1&nbsp;=&nbsp;half&nbsp;+&nbsp;1;添加的结果是INT_MAX。(INT_MAX总是一个奇数)。所以这是有效的输入。但是在你的日常生活中INT_MAX - half == half1,你将会中止。误报。可以通过放入<而不是<=在两个检查中修复此错误。但是那时你的代码也不是最优的。以下是:int&nbsp;add(int&nbsp;lhs,&nbsp;int&nbsp;rhs){ &nbsp;if&nbsp;(lhs&nbsp;>=&nbsp;0)&nbsp;{ &nbsp;&nbsp;if&nbsp;(INT_MAX&nbsp;-&nbsp;lhs&nbsp;<&nbsp;rhs)&nbsp;{ &nbsp;&nbsp;&nbsp;/*&nbsp;would&nbsp;overflow&nbsp;*/ &nbsp;&nbsp;&nbsp;abort(); &nbsp;&nbsp;} &nbsp;} &nbsp;else&nbsp;{ &nbsp;&nbsp;if&nbsp;(rhs&nbsp;<&nbsp;INT_MIN&nbsp;-&nbsp;lhs)&nbsp;{ &nbsp;&nbsp;&nbsp;/*&nbsp;would&nbsp;overflow&nbsp;*/ &nbsp;&nbsp;&nbsp;abort(); &nbsp;&nbsp;} &nbsp;} &nbsp;return&nbsp;lhs&nbsp;+&nbsp;rhs;}要看到这是有效的,你必须象征性地lhs在不等式的两边添加,这给出了你的结果超出范围的算术条件。
随时随地看视频慕课网APP
我要回答