不同版本的gcc不同地编译相同的代码

时间:2016-04-09 09:34:26

标签: c++ gcc undefined-behavior

我已经从MS Visual Studio切换到gcc,目前我正在尝试重新编译我在VS中用gcc编写的一些代码。现在我遇到了一些奇怪的事情。简单解释一下,请考虑以下代码,但首先请注意,我已经知道它是一个非常糟糕的代码(这不是重点)

#include <iostream>

int main()
{
    int i = 0,
        a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
        b[10] = { 6, 5, 4, 1, 3, 2, 9, 7, 10, 8 };

    while (i ^ 5)   a[i++] = a[i] + b[i];

    while (i ^ 10)  a[i++] = a[i] - b[i];

    for (int j = 0; j < 10; j++)
        std::cout << a[j] << ' ';
}

当我使用Visual Studio编译它时,它会导致:

7 7 7 5 8 4 -2 1 -1 2 

正如所料。使用gcc v.4.3.6,我也得到了相同的结果(Live example)。

但是当我切换到gcc 5.3.0时,它会导致:

7 7 5 8 8 -2 1 -1 2 -4198061

在生成许多关于未定义行为的警告之后。

问题是,为什么visual studio,即使在最新版本中,也不关心代码质量和未定义的行为,以及为什么早期版本的gcc也会这样做?最新版本的gcc发生了什么?

3 个答案:

答案 0 :(得分:4)

本主题在C ++ 11标准的第1.9 / 15节(程序执行)中讨论:

  

除非另有说明,否则对单个运算符的操作数和单个表达式的子表达式的评估是不确定的。 [注意:在执行期间多次评估的表达式   对于一个程序,其子表达式的未经测序和不确定顺序的评估不需要在不同的评估中一致地执行。 - 结束注释]的操作数的值计算   在运算符结果的值计算之前对运算符进行排序。如果相对于相同标量对象的另一个副作用或值计算,对标量对象的副作用未被排序   使用相同标量对象的值,它们不可能并发(1.10),行为未定义。 ...

void g(int i, int* v) {
    i = v[i++];       // the behavior is undefined
    i = 7, i++, i++;  // i becomes 9
    i = i++ + 1;      // the behavior is undefined
}

未定义的行为意味着:任何事情都可能发生,程序可能会按照您的预期行事或某些事情(正如您所说)可能会发生“奇怪”。

另请参阅:"Sequence point" on Wikipedia

  

...取决于表达式评估的顺序,增量可以在赋值之前,之后或交错之后发生。 ...

快速解决方法是改变

while (i ^ 5)   a[i++] = a[i] + b[i];

while (i ^ 5)   a[i] = a[i] + b[i], i++;

答案 1 :(得分:1)

在我看来,这行代码是未定义的行为:

a[i++] = a[i] + b[i];

这可能意味着:

a[i] = a[i] + b[i];
i++;

或者:

a[i] = a[i + 1] + b[i + 1];
i++;

似乎两个第一个编译器使用第一个解释,而第三个编译器使用第二个解释。

答案 2 :(得分:-1)

'while(i ^ 5)a [i ++]' - 我修改了两次,没有序列点,所以UB。

'评估&amp;&amp;和&amp;&amp ;;的左右操作数之间(逻辑AND),|| (逻辑或)' - 没有提到异或。

也许.......