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

时间:2015-03-28 05:00:14

标签: c gcc gcc-warning

我正在阅读一本书中的一些代码,当我决定在sec语句之前进行更改以查看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;
}

这在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

但是当我评论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;
}

现在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!

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

2 个答案:

答案 0 :(得分:8)

显然gcc有一个长期存在的错误否定问题,此选项请参阅此错误报告:Bug 18501 - [4.8/4.9/5 Regression] Missing 'used uninitialized' warning (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);

你也会收到警告,Marc Glisse也指出删除前四个printf同样有用( see it live )。我没有在重复项中看到类似的示例,但不确定是否可以为此添加另一个错误报告。

另见Better Uninitialized Warnings说:

  

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

附注,clang似乎很好地抓住了这个案例( see live example ):

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

正如我在下面的评论中指出的那样,至少这个案例似乎已在gcc 5.0修复。

答案 1 :(得分:5)

这一行:

scanf("%d",&sec);

GCC知道您在函数中初始化sec 某处

作为一个单独的步骤,GCC可以进行流量分析,以确定在使用之前是否初始化sec。遗憾的是,GCC并不总能进行您想要的流量分析,甚至根本不进行流量分析。有时流量分析是在其他信息不可用的阶段完成的。所以,你不能指望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在检测到这些错误方面非常糟糕。