不,这不是如何检测整数溢出的副本?. 问题是一样的,但问题是不同的。
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++ 标准是否被破坏?
补充说明: 对不起,您可能误解了我的问题。我不是在问如何解决这个问题——这已经在别处得到了回答。我在问一个关于 C 标准的更基本的问题。如果没有有用且可靠的方法来检查溢出,那么语言本身就是可疑的。例如,如果我创建一个带有边界检查的安全数组类,那么我应该是安全的,但如果边界检查可以被优化掉,我就不安全了。
如果标准允许这种情况发生,那么要么标准需要修订,要么标准的解释需要修订。
添加注释 2: 这里的人们似乎不愿意讨论“未定义行为”的可疑概念。C99 标准列出了 191 种不同类型的未定义行为(链接)这一事实表明标准草率。
许多程序员欣然接受“未定义的行为”允许做任何事情的声明,包括格式化你的硬盘。我认为标准将整数溢出归入与写入数组边界外相同的危险类别是一个问题。
为什么这两种“未定义行为”不同?因为:
许多程序依赖于整数溢出是良性的,但是当你不知道那里有什么时,很少有程序依赖于写入数组边界之外的内容。
实际上,在数组边界之外写入可能会造成与格式化硬盘一样糟糕的事情(至少在像 DOS 这样的未受保护的操作系统中),并且大多数程序员都知道这是危险的。
当您将整数溢出放入危险的“任何事情发生”类别时,它允许编译器做任何事情,包括谎报它正在做的事情(在溢出检查被优化的情况下)
使用调试器可以发现诸如写入超出数组边界之类的错误,但优化掉溢出检查的错误却不能,因为在调试时优化通常是关闭的。
在整数溢出的情况下,gcc 编译器显然会避免“任何事情发生”的策略。在许多情况下,它会避免优化,例如循环,除非它可以验证溢出是不可能的。出于某种原因,gcc 人员已经认识到,如果他们在这里遵循“一切顺利”的政策,我们将会有太多错误,但他们对优化掉溢出检查的问题有不同的态度。
也许这里不适合讨论这些哲学问题。至少,这里的大多数答案都是离题的。有没有更好的地方来讨论这个?