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

时间:2014-05-28 08:26:35

标签: c++ c gcc compiler-optimization integer-overflow

我有这段代码:

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

有人可以向我解释这种行为吗?

3 个答案:

答案 0 :(得分:7)

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

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

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

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

答案 1 :(得分:2)

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

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

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

您的选择一般是:

  1. 让自己满意UB不会发生,并忽略警告。
  2. 允许UB发生并承担后果。
  3. 重写代码,以便UB真的不会发生,编译器知道它不会发生,并且警告应该消失。
  4. 我会推荐最后一个选项。 ab的简单范围测试应该足够好了。


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

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

    我们可能还会注意到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标准)。改变正确,可移植行为的优化是不合格的优化。