C ++ while循环优化不能正常工作

我有这段代码:

#include  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,它必须小于另一个整数。 如果它更大,则循环终止。

任何人都可以向我解释这种行为吗?

C ++标准规定,如果有符号整数计算产生的结果超出了该类型的可表示范围,则行为未定义。 整数溢出是UB。 一旦UB发生,实施可以随意做任何事情。

许多编译器对UB不会发生的明确假设进行优化。 [或者如果确实如此,代码可能是错误的,但这是你的问题!]

此编译器通知您它正在将此类优化应用于计算,在该计算中,无法通过分析代码确定UB未发生。

您的选择一般是:

  1. 让自己满意UB不会发生,并忽略警告。
  2. 允许UB发生并承担后果。
  3. 重写代码,以便UB真的不会发生,编译器知道它不会发生,并且警告应该消失。

我会推荐最后一个选项。 aba简单范围测试应该足够好。


我的猜测是编译器发出此错误,因为循环处理完全未知的值,并且它无法很好地分析数据流以确定UB是否可能发生。

我们凭借优越的推理能力可以说服自己UB不会发生,所以我们可以忽略错误。 事实上,仔细阅读错误信息可能会让我们询问它是否相关。 这两个常数值C1C2哪里?

我们可能还会注意到a永远不会消极,所以为什么循环中的测试呢? 我可能会重写代码以抑制错误,(但从经验来看,这可能是一种弄巧成拙的练习)。 试试这个,看看会发生什么(并避免不必要的括号杂乱):

 if (a >= 0) { while (a < b) { ... ++a; } } 

编译器正在进行优化以将a + 1 < b转换a < b - 1 。 但是,如果bINT_MIN那么这将是下溢,这是行为的变化。 这是它的警告。

当然,您可能会说这是不可能的,但编译器的资源有限,无法对数据路径进行深入分析。

添加b >= 0的检查可以解决问题。

编辑 :另一种可能性是它将a >= 0移动到循环外部,因为(假设没有溢出)它永远不会改变。 同样,该假设可能对所有输入都无效(即,如果b为负)。 您需要检查最终assembly以查看它实际执行的操作。

编译器警告你的是它假设在原始代码中没有发生签名溢出

警告并不意味着“我即将编写一个可能引入溢出的优化。”

换句话说,如果您的程序依赖于溢出(即,不是高度可移植的),那么编译器正在进行的优化可能会改变其行为。 (因此,请自行validation此代码不依赖于溢出)。

例如,如果你有“a + b> c”,并且你依赖于这个测试在+ b算术包围时失败(典型的二进制补码行为),那么如果这恰好被代数转换为“a> c – b“,那么它可能会成功,因为c – b可能不会溢出,并产生一个小于a的值。

注意:只有C程序可以调用未定义的行为。 当编译器不正确时,它们是“不合格的”。 如果编译器符合(C标准)符合代码的错误,则编译器只能是不符合(对C标准)。 改变正确的可移植行为的优化是不合格的优化。