6

当我教 C 时,有时我指望 GCC 来做一些规则的“令人信服”的部分。例如,不应考虑函数上的局部变量在调用之间保留值。

GCC 总是帮助我向学生教授这些课程,将垃圾放在局部变量上,以便他们了解正在发生的事情。

现在,这段代码肯定让我很难受。

#include <stdio.h>

int test(int x)
{
        int y;
        if(!x)
                y=0;
        y++;
        printf("(y=%d, ", y);
        return y;
}

int main(void)
{
        int a, i;

        for(i=0; i<5; i++)
        {
                a=test(i);
                printf("a=%d), ", a);
        }
        printf("\n");
        return 0;
}

输出是:

(y=1, a=1), (y=2, a=2), (y=3, a=3), (y=4, a=4), (y=5, a=5),

但如果我评论这一行:

       /* printf("(y=%d, ", y); */

然后输出变为:

a=1), a=32720), a=32721), a=32722), a=32723),

我使用 switch 编译代码-Wall,但没有警告与使用局部变量相关,而没有初始化它们。

是否有任何 GCC 开关会导致警告,或者至少显式显示一些垃圾?我尝试了优化开关,这有助于代码输出变成这样:

$ gcc test.c -o test -Wall -Os
$ ./test 
(y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1),
$ gcc test.c -o test -Wall -Ofast
$ ./test 
(y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1),
$ gcc test.c -o test -Wall -O0
$ ./test 
(y=1, a=1), (y=2, a=2), (y=3, a=3), (y=4, a=4), (y=5, a=5),
$ gcc test.c -o test -Wall -O1
$ ./test 
(y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1),
$ gcc test.c -o test -Wall -O2
$ ./test 
(y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1),
$ gcc test.c -o test -Wall -O3
$ ./test 
(y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1),

但是 y=1 在所有情况下都是一种技巧。标准是否改变了,所以局部变量现在用零初始化?

4

3 回答 3

4

这就是未定义行为的问题:它是“未定义的”。

因此,任何一组结果都完全取决于编译器/设置/内存中的内容/中断的组合。

您可能会遇到一些输出您“期望”的设置来证明问题 - 但这只是运气。

你所发现的实际上更重要——故障模式的数量比你想象的要多(尽管幸运的是,还没有人重新格式化你的硬盘驱动器),并且最有害和最危险的“未定义行为”类型是其中行为实际上在 99.99% 的时间内“符合预期”。

是 0.01% 得到你。

于 2013-06-13T18:47:42.923 回答
2

您的程序会导致未定义的行为。如果将非零值传递给test(),则y永远不会被初始化。 printf包含与否,您不能依赖结果。

如果你想要一个警告,clang 会给你一个-Wsometimes-uninitialized

example.c:6:12: warning: variable 'y' is used uninitialized whenever 'if'
      condition is false [-Wsometimes-uninitialized]
        if(!x)
           ^~
example.c:8:9: note: uninitialized use occurs here
        y++;
        ^
example.c:6:9: note: remove the 'if' if its condition is always true
        if(!x)
        ^~~~~~
example.c:5:14: note: initialize the variable 'y' to silence this warning
        int y;
             ^
              = 0
1 warning generated.

我用我手头的几个 GCC 版本进行了测试,但它们都不会对我产生警告。

于 2013-06-13T18:35:02.117 回答
2

这个想法的另一种可能的方法可能是在调用函数之间调用另一个test函数。如果另一个函数使用堆栈空间,那么它可能最终会更改堆栈值。例如,也许添加这样的函数:

int changeStack( int x )
{
    int y2 = x + 100;
    return y2;
}

然后添加对它的调用:

    for(i=0; i<10; i++)
    {
            a=test(i);
            printf("a=%d), ", a);
            changeStack( i );
    }

它当然取决于优化级别,但是使用默认的 gcc compile ( gcc test.c),我在更改它之后得到以下结果:

(y=1, a=1), (y=101, a=101), (y=102, a=102), (y=103, a=103), (y=104, a=104), (y=105, a=105), (y=106, a=106), (y=107, a=107), (y=108, a=108), (y=109, a=109),
于 2013-06-13T18:45:34.607 回答