没有有用和可靠的方法来检测C/C++中的整数溢出?

A F*_*Fog 11 c c++ overflow integer-overflow gcc4

不,这不是如何检测整数溢出的重复.问题是一样的,但问题是不同的.


gcc编译器可以优化掉溢出检查(使用-O2),例如:

int a, b;
b = abs(a);                     // will overflow if a = 0x80000000
if (b < 0) printf("overflow");  // optimized away
Run Code Online (Sandbox Code Playgroud)

gcc人认为这不是一个错误.根据C标准,溢出是未定义的行为,它允许编译器执行任何操作.显然,任何事情都包括假设溢出永远不会发生.不幸的是,这允许编译器优化掉溢出检查.

最近的CERT文件中描述了检查溢出的安全方法.本文建议在添加两个整数之前执行类似的操作:

if ( ((si1^si2) | (((si1^(~(si1^si2) & INT_MIN)) + si2)^si2)) >= 0) { 
  /* handle error condition */
} else {
  sum = si1 + si2;
}
Run Code Online (Sandbox Code Playgroud)

显然,当你想确保结果有效时,你必须在一系列计算中的每个+, - ,*,/和其他操作之前做这样的事情.例如,如果要确保数组索引不受限制.这太麻烦了,几乎没有人这样做.至少我从未见过一个系统地执行此操作的C/C++程序.

现在,这是一个根本问题:

  • 在访问阵列之前检查数组索引很有用,但不可靠.

  • 使用CERT方法检查一系列计算中的每个操作都是可靠但无用的.

  • 结论:在C/C++中没有有用且可靠的方法来检查溢出!

我拒绝相信这是在编写标准时的意图.

我知道有一些命令行选项可以解决问题,但这并没有改变我们对标准或其当前解释存在根本问题的事实.

现在我的问题是:gcc的人服用的"未定义行为"的解释时,它允许他们优化掉溢出检查,否则过犹不及是C/C++标准坏了吗?

补充说明: 对不起,您可能误解了我的问题.我不是问如何解决这个问题 - 这已经在其他地方得到了回答.我在问一个关于C标准的更基本的问题.如果没有有用且可靠的检查溢出的方法,那么语言本身就是可疑的.例如,如果我使用边界检查创建一个安全的数组类,那么我应该是安全的,但是如果边界检查可以被优化掉,我就不会这样做.

如果标准允许这样做,则标准需要修订或标准需求修订的解释.

补充说明2: 这里的人似乎不愿意讨论"未定义行为"的可疑概念.C99标准列出了191种不同的未定义行为(链接)这一事实表明了一种草率的标准.

许多程序员都乐于接受"未定义行为"赋予许可做任何事情的声明,包括格式化硬盘.我认为标准将整数溢出放入与写入数组边界相同的危险类别是一个问题.

为什么这两种"未定义的行为"有所不同?因为:

  • 许多程序依赖于整数溢出是良性的,但是当你不知道那里有什么时,很少有程序依赖于写出数组边界.

  • 写作之外数组边界实际上可以做的那么糟糕,因为(像DOS未受保护的操作系统至少)格式化您的硬盘上的东西,大多数程序员都知道,这是危险的.

  • 当你将整数溢出放入危险的"任何事情"类别时,它允许编译器做任何事情,包括说谎它正在做什么(在优化溢出检查的情况下)

  • 使用调试器可以找到诸如写入数组边界之类的错误,但是优化掉溢出检查的错误不能,因为优化通常在调试时关闭.

  • 在整数溢出的情况下,gcc编译器明显地避免了"任何进展"策略.在许多情况下,它不会优化例如循环,除非它可以验证溢出是不可能的.出于某种原因,海湾合作委员会的人已经认识到,如果他们遵循"怎么都行"的政策在这里我们有太多的失误,但他们有不同的态度,以优化掉溢出检查的问题.

也许这不是讨论这些哲学问题的正确场所.至少,这里的大多数答案都是不合适的.有没有更好的地方来讨论这个?

Kon*_*lph 3

问问自己:您实际上需要多久检查一次算术?如果您经常需要它,您应该编写一个checked_int类来重载公共运算符并将检查封装到此类中。在开源网站上分享实现的道具。

\n\n

更好的是(可以说),使用一个big_integer类,这样溢出就不会首先发生\xe2\x80\x99。

\n