为什么GCC有时仅在初始化之前检测到变量的使用?

tia*_*dou 9 c gcc gcc-warning

我正在阅读一本书中的一些代码,当我决定进行修改以查看secwhile声明之前未初始化的值是什么:

#include<stdio.h>

#define S_TO_M 60

int main(void)
{
    int sec,min,left;

    printf("This program converts seconds to minutes and ");
    printf("seconds. \n");
    printf("Just enter the number of seconds. \n");
    printf("Enter 0 to end the program. \n");
    printf("sec = %d\n",sec);
    while(sec > 0)
    {
        scanf("%d",&sec);
        min = sec/S_TO_M;
        left = sec % S_TO_M;
        printf("%d sec is %d min, %d sec. \n",sec,min,left);
        printf("Next input?\n");
    }
    printf("Bye!\n");

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这在GCC下编译没有任何警告,即使sec在那时未初始化,我得到的值为32767:

$ gcc -Wall test.c
$ ./a.out 
This program converts seconds to minutes and seconds. 
Just enter the number of seconds. 
Enter 0 to end the program. 
sec = 32767
Run Code Online (Sandbox Code Playgroud)

但是当我发表while声明时:

#include<stdio.h>

#define S_TO_M 60

int main(void)
{
    int sec;
    //min,left;

    printf("This program converts seconds to minutes and ");
    printf("seconds. \n");
    printf("Just enter the number of seconds. \n");
    printf("Enter 0 to end the program. \n");
    printf("sec = %d\n",sec);
    /*
    while(sec > 0)
    {
        scanf("%d",&sec);
        min = sec/S_TO_M;
        left = sec % S_TO_M;
        printf("%d sec is %d min, %d sec. \n",sec,min,left);
        printf("Next input?\n");
    }
    */
    printf("Bye!\n");

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

现在GCC发出警告并sec最终归零:

$ gcc -Wall test.c
test.c: In function ‘main’:
test.c:12:8: warning: ‘sec’ is used uninitialized in this function[-Wuninitialized]
printf("sec = %d\n",sec);
    ^
$ ./a.out
This program converts seconds to mintutes and seconds. 
Just enter the number of seconds. 
Enter 0 to end the program. 
sec = 0
Bye!
Run Code Online (Sandbox Code Playgroud)

为什么警告第二次显示但不是第一次显示?

Sha*_*our 8

显然gcc有一个长期运行的问题与此选项的错误否定看到此错误报告:错误18501 - [4.8/4.9/5回归]缺少'使用未初始化'警告(CCP)和长重复列表:

重复:30542 30575 30856 33327 36814 37148 38945 39113 40469 42724 42884 45493 46684 46853 47623 48414 48643 49971 56972 57629 58323 58890 59225 60444

注意到最后的评论,我们可以看到这已经是一个持续的问题超过十年:

嘿伙计们,今年将是这个bug的10周年纪念日.我们应该点蛋糕!

请注意,如果您删除:

scanf("%d",&sec);
Run Code Online (Sandbox Code Playgroud)

你也会收到一个警告,Marc Glisse也指出删除前四个printf也有效(见现场直播).我没有在重复项中看到类似的示例,但不确定是否为此添加了另一个错误报告.

另请参阅更好的未初始化警告,其中说:

GCC能够警告用户使用未初始化变量的值.这样的值是未定义的,它永远不会有用.它甚至不能用作随机值,因为它很少是随机值.不幸的是,在一般情况下,检测何时使用未初始化的变量等同于解决停止问题.GCC尝试使用优化器收集的信息检测某些实例,并在命令行中给出选项-Wuninitialized时对其进行警告.目前的实施存在许多缺点.首先,它仅在通过-O1,-O2或-O3启用优化时有效.其次,误报或否定的集合根据启用的优化而变化.这也会导致在发布之间添加或修改优化时报告的警告的高度可变性.

旁注,clang似乎很好地抓住了这个案例(见实例):

 warning: variable 'sec' is uninitialized when used here [-Wuninitialized]
printf("sec = %d\n",sec);
                    ^~~
Run Code Online (Sandbox Code Playgroud)

正如我在下面的评论中指出的那样,至少这个案例似乎是固定的gcc 5.0.


Die*_*Epp 5

有了这条线:

scanf("%d",&sec);
Run Code Online (Sandbox Code Playgroud)

GCC知道你在函数中的sec 某个地方初始化.

作为一个单独的步骤,GCC可以进行流量分析,以确定sec在使用之前是否初始化.遗憾的是,GCC并不总能进行您想要的流量分析,甚至根本不进行流量分析.有时流量分析是在其他信息不可用的阶段完成的.所以,你不能指望海湾合作委员会弄明白.

其他编译器会搞清楚.例如,

~ $ clang-3.7 -c -Wall -Wextra -O2 ex.c                      
ex.c:11:25: warning: variable 'sec' is uninitialized when used here
      [-Wuninitialized]
    printf("sec = %d\n",sec);
                        ^~~
ex.c:5:12: note: initialize the variable 'sec' to silence this warning
    int sec,min,left;
           ^
            = 0
1 warning generated.

恰好GCC在检测这些错误方面非常糟糕.

  • 实际上,`scanf`阻止将`sec`视为一个值(即忘记它的地址),而tree-ssa-uninit.c包含一个注释,说它应该使用`walk_aliased_vdefs`(做一些流/别名分析)但是没有所以它认为早期的'printf`可以写成`sec`. (3认同)