我有这段代码:
#include <stdio.h>
int main(int argc, const char** argv)
{
int a = argv[0][0];
int b = argv[0][1];
while ((a >= 0) &&
(a < b))
{
printf("a = %d\n", a);
a++;
}
return 0;
}
我正在用gcc-4.5 -02 -Wstrict-overflow=5
编译它。
编译器对我大吼大叫
warning: assuming signed overflow does not occur when changing X +- C1 cmp C2 to X cmp C1 +- C2
这究竟意味着什么?
如果我是正确的,这个循环永远不会导致溢出,因为要增加a,它必须小于另一个整数。如果它更大,则循环终止。
有人可以向我解释这种行为吗?
答案 0 :(得分:7)
编译器正在进行优化,以将a + 1 < b
转换为a < b - 1
。
但是,如果b
为INT_MIN
,那么这将会下溢,这是行为的变化。
这是它的警告。
当然,您可以说这是不可能的,但编译器的资源有限,无法对数据路径进行深入分析。
添加b >= 0
可以解决问题的检查。
编辑 :另一种可能性是它将a >= 0
移动到循环外部,因为(假设没有溢出)它永远不会改变。同样,该假设可能对所有输入都无效(即,如果b
为负)。您需要检查最终装配,看看它实际上做了什么。
答案 1 :(得分:2)
C ++标准规定,如果有符号整数计算产生的结果超出了该类型的可表示范围,则行为未定义。整数溢出是UB。一旦UB发生,实施就可以随意做任何事情了。
许多编译器对UB未发生的明确假设进行优化。 [或者,如果确实如此,代码可能是错误的,但这是你的问题!]
此编译器通知您它正在将此类优化应用于无法通过分析代码确定UB未发生的计算。
您的选择一般是:
我会推荐最后一个选项。 a
和b
的简单范围测试应该足够好了。
我的猜测是编译器发出此错误,因为循环处理完全未知的值,并且它无法很好地分析数据流以确定UB是否可能发生。
我们凭借优越的推理能力可以说服自己UB不会发生,所以我们可以忽略错误。事实上,仔细阅读错误信息可能会让我们询问它是否相关。这两个常数值C1
和C2
在哪里?
我们可能还会注意到a
永远不会消极,所以为什么循环中的测试呢?我可能会重写代码以抑制错误,(但从经验来看,这可能是一个弄巧成拙的练习)。试试这个,看看会发生什么(并避免不必要的括号杂乱):
if (a >= 0) {
while (a < b) {
...
++a;
}
}
答案 2 :(得分:0)
编译器警告您的是,假设原始代码中没有签名溢出。
警告并不意味着“我即将编写可能引入溢出的优化。”
换句话说,如果您的程序依赖于溢出(即,不是高度可移植的),那么编译器正在进行的优化可能会改变其行为。 (因此,请自行验证此代码不依赖于溢出)。
例如,如果你有“a + b&gt; c”,并且你依赖于这个测试在+ b算术回绕时失败(典型的二进制补码行为),那么如果这恰好被代数转换为“a&gt; c - b”,然后它可能会成功,因为c - b可能不会溢出,并产生小于a的值。
注意:只有C程序可以调用未定义的行为。当编译器不正确时,它们是“不合格的”。如果编译器符合(C标准)符合代码的错误,编译器只能是不符合(对C标准)。改变正确,可移植行为的优化是不合格的优化。