使用未初始化的变量和编译器(GCC)的乐趣

时间:2011-02-02 19:43:09

标签: c++ gcc compiler-construction undefined-behavior initialization

C ++标准的§3.9.1/ 6部分说,

  

bool类型的值可以是truefalse

现在考虑一下这段代码,

void f(bool b)
{
    switch(b) //since b is bool, it's value can be either true or false!
    {
        case true: cout << "possible value - true";  break;
        case false: cout << "possible value - false"; break;
        default: cout << "impossible value";
    }
}
int main()
{
    bool b; //note : b is uninitialized
    f(b);
    return 0;
}

编译F:\workplace>g++ test.cpp -pedantic

运行。输出:

  

不可能的价值

意外输出?嗯,不是真的,因为标准在§3.9.1/ 6的脚注中写道:

  

以所述方式使用bool值   按此国际标准   “未定义”,,例如,通过检查a的值   未初始化的自动对象,   可能会导致表现得好像   既不真实也不虚假

所以无论我编译和运行这个程序多少次,我都得到相同的输出:impossible value。但是,如果我稍微更改它 - 从图片中删除函数f(),并在switch本身写下main()块:

int main()
{
    bool b; //note : b is uninitialized
    switch(b) //since b is bool, it's value can be either true or false!
    {
        case true: cout << "possible value - true";  break;
        case false: cout << "possible value - false"; break;
        default: cout << "impossible value";
    }
    return 0;
}

然后我编译并运行这个程序,我没有得到impossible value作为输出;无论我重复多少次,我都没有得到impossible value

我只是想知道为什么这个突然改变未初始化的bool的行为?

嗯,从语言的角度来看,很清楚:行为是未定义的。我明白这一点。我也明白编译器可以自由地做任何事情。但是,从编译器的角度来看,这对我来说似乎很有趣。 编译器(即GCC)在每种情况下可能做什么以及为什么?

我正在使用:g++ (GCC) 4.5.0 - MinGW, on Windows 7 Basic, 64-bit OS

2 个答案:

答案 0 :(得分:17)

  

我只是想知道为什么未初始化的bool的这种突然变化?

反汇编代码,看看编译器在做什么。

我的猜测:由于该值现在仅在本地使用,因此编译器会完全优化它。由于行为未定义,编译器可以安全地假设任何值,例如, false。这是一个非常明显的优化,因为b的值就编译器而言是恒定的,并且switch的整个逻辑是多余的。那么为什么要把它放在可执行文件中呢?

(这里重要的一点是b只在第二个代码中本地使用,而且即使在未经优化的代码中也会触发更多优化。第一个代码必须在编译器之前内联做任何这样的优化,或者必须跟踪代码路径,这不是微不足道的。)

答案 1 :(得分:1)

就在今天,我遇到了这个bug的一个版本。我在这里提供我的经验,以防它对任何人有启发。

我有一些代码可以归结为

if(!(a == b && c.d())) { do_something(); }

我追逐的错误是do_something()正在发生,错误地发生。然而,a肯定等于b,而c.d()似乎也是如此。

当我跟踪这个时,我暂时添加了这些测试打印输出:

if(  a == b && c.d() ) printf("yes\n"; else printf("no\n");
if(!(a == b && c.d())) printf("noo\n"; else printf("yess\n");

令我惊讶的是,这打印了yesnoo,它确认了do_something发生的原因,以及发生了一些非常奇怪的事情。

事实证明,方法d()类似于

bool whatever::d() {
    return _successful;
}

_successful未初始化。当我打印出它的值时,它是236,这就是为什么早些时候我曾说过“c.d(),它似乎是真的。”

我没有检查汇编代码,但我猜测在某些情况下,gcc正在测试它是否为非零,但在其他情况下,它只是测试低阶位。

正确初始化_successful会让错误消失。 (由于早期的程序员首先编写了方法d(),因此它已经未被初始化了十年。但是这个错误直到几个月前才出现。这就是为什么,有时候,软件是硬。)