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

我正在阅读一本书中的一些代码,当我决定在while语句之前进行更改以查看sec的未初始化值是什么while

 #include #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 #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! 

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

显然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); 

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

另请参阅Better Uninitialized Warnings ,其中说:

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

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

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

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

有了这条线:

 scanf("%d",&sec); 

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

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

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

 〜$ clang-3.7 -c -Wall -Wextra -O2 ex.c                      
例如:11:25:警告:变量'sec'在这里使用时未初始化
       [-Wuninitialized]
     printf(“sec =%d \ n”,sec);
                         ^ ~~
 ex.c:5:12:注意:初始化变量'sec'以使此警告静音
     int sec,min,left;
            ^
             = 0
产生1个警告。

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