不,这不是重复的 如何检测整数溢出?。问题是一样的,但问题是不同的。
gcc编译器可以优化掉溢出检查(使用-O2),例如:
int a, b;
b = abs(a); // will overflow if a = 0x80000000
if (b < 0) printf("overflow"); // optimized away
gcc人认为这不是一个错误。根据允许编译器执行的C标准,溢出是未定义的行为 什么。显然, 什么 包括假设溢出永远不会发生。不幸的是,这允许编译器优化掉溢出检查。
最近描述了检查溢出的安全方法 CERT论文。本文建议在添加两个整数之前执行类似的操作:
if ( ((si1^si2) | (((si1^(~(si1^si2) & INT_MIN)) + si2)^si2)) >= 0) {
/* handle error condition */
} else {
sum = si1 + si2;
}
显然,当你想确保结果有效时,你必须在一系列计算中的每个+, - ,*,/和其他操作之前做这样的事情。例如,如果要确保数组索引不受限制。这太麻烦了,几乎没有人这样做。至少我从未见过一个系统地执行此操作的C / C ++程序。
现在,这是一个根本问题:
在访问阵列之前检查数组索引很有用,但不可靠。
使用CERT方法检查一系列计算中的每个操作都是可靠但无用的。
结论:在C / C ++中没有有用且可靠的方法来检查溢出!
我拒绝相信这是在编写标准时的意图。
我知道有一些命令行选项可以解决问题,但这并没有改变我们对标准或其当前解释存在根本问题的事实。
现在我的问题是: 当gcc人员允许他们优化掉溢出检查,或者C / C ++标准被破坏时,gcc人员是否对“未定义行为”的解释太过分了?
补充说明: 对不起,您可能误解了我的问题。我不是问如何解决这个问题 - 这已经得到了回答 别处。我在问一个关于C标准的更基本的问题。如果没有有用且可靠的方法来检查溢出,那么语言本身就是可疑的。例如,如果我使用边界检查创建一个安全的数组类,那么我应该是安全的,但是如果边界检查可以被优化掉的话我不会。
如果标准允许这样做,则标准需要修订或标准需求修订的解释。
补充说明2: 这里的人们似乎不愿意讨论“未定义行为”的可疑概念。事实上,C99标准列出了191种不同的未定义行为(链接)是标准草率的标志。
许多程序员都乐于接受“未定义的行为”赋予许可做任何事情的声明,包括格式化硬盘。我认为标准将整数溢出放入与写入数组边界相同的危险类别是一个问题。
为什么这两种“未定义的行为”不同?因为:
许多程序依赖于整数溢出是良性的,但是当你不知道那里有什么时,很少有程序依赖于写出数组边界。
实际上在数组边界外写 能够 做一些像格式化硬盘一样糟糕的事情(至少在DOS等不受保护的操作系统中),并且大多数程序员都知道这很危险。
当你将整数溢出放入危险的“任何事情”类别时,它允许编译器做任何事情,包括说谎它正在做什么(在优化溢出检查的情况下)
使用调试器可以找到诸如写入数组边界之类的错误,但是优化掉溢出检查的错误不能,因为优化通常在调试时关闭。
在整数溢出的情况下,gcc编译器明显地避免了“任何事情”政策。在许多情况下,它避免优化,例如一个循环,除非它可以验证溢出是不可能的。出于某种原因,gcc人已经认识到如果他们遵循“任何事情”政策,我们会有太多错误,但他们对优化掉溢出检查的问题有不同的态度。
也许这不是讨论这些哲学问题的正确场所。至少,这里的大多数答案都是不合适的。有没有更好的地方来讨论这个?