GCC不能警告未初始化的变量

22
以下代码存在一个可能未初始化的变量。看起来gcc应该生成一个警告,但实际上没有:
$ cat a.c
int foo(int b) {
  int a;
  if (b)
    a = 1;
  return a;
}

$ gcc-4.7 -c -Wall -Wmaybe-uninitialized -o a.o ./a.c
$ gcc-4.7 -v
Using built-in specs.
COLLECT_GCC=gcc-4.7
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.7/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.7.3-2ubuntu1~12.04' --with-bugurl=file:///usr/share/doc/gcc-4.7/README.Bugs --enable-languages=c,c++,go,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.7 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.7 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --with-system-zlib --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.7.3 (Ubuntu/Linaro 4.7.3-2ubuntu1~12.04)

有什么线索可以让gcc报告未初始化的变量吗?


编译器有没有足够聪明,能看出 b 在其他地方被设置为始终非零? - Michael Dorgan
@MichaelDorgan,在OP的示例中没有其他代码。 - Carl Norum
没听清楚。我假设如果没有定义“main”函数,那么一定有其他的代码... - Michael Dorgan
嗯,要成为一个真正的程序,确实需要其他代码。但在这个编译器调用/翻译单元的上下文中并没有其他代码,因此这就是与这个问题直接相关的所有代码。 - Carl Norum
当您使用“-O1”进行编译时会发生什么? - jxh
显示剩余2条评论
4个回答

16

看起来您无法查看这个错误报告。(以及这个,它被标记为与那个一样的副本 - 它具有与您的测试用例相同的内容。)由于根本原因的错误已经存在了将近10年的时间,这似乎不是一个容易解决的问题。事实上,我链接的第二个bug在讨论中有“永远不会修复”的短语,所以看起来情况不太好。

如果这对您真的很重要,clang确实使用-Wsometimes-uninitialized捕获此错误,该选项包含在-Wall中:

a.c:3:7: warning: variable 'a' is used uninitialized whenever 'if' condition is
      false [-Wsometimes-uninitialized]
  if (b)
      ^
a.c:5:10: note: uninitialized use occurs here
  return a;
         ^
a.c:3:3: note: remove the 'if' if its condition is always true
  if (b)
  ^~~~~~
a.c:2:8: note: initialize the variable 'a' to silence this warning
  int a;
       ^
        = 0
1 warning generated.

1
我的clang(版本3.0)不认识“-Wsometimes-uninitialized”和“-Wmaybe-uninitialized”。我能够通过使用“-Wconditional-uninitialized”或“-Weverything”来生成上述警告。 - clandau
1
Clang 3.0相当古老。 - Carl Norum

6

您需要启用某种优化。例如,使用-O2重新编译您的示例,警告将出现。

由于这需要对代码路径进行分析,而且是一项相当昂贵的计算,因此只有在要求优化代码时,GCC才会启用它。


1
这对我有用。不幸的是,我没有离开-O2就无法获得这种行为。 - winduptoy
使用GCC 12,您可以通过-O1或更高级别来获取它。 - Yasushi Shoji

5
问题在于gcc不知道您将使用零参数调用该函数。在这种情况下,您进行测试的事实可能是一个很好的提示,表明您有时打算这样做,但一般情况要难得多。考虑以下情况:
int foo(int b) {
  int a;
  switch(b) {
  case 1:
    a = 1;
    break;
  case 2:
    a = 0;
    break;
  case 3:
    a = 2;
    break;
  }
  return a;
}

这将是一个完全合理的函数,其接口契约规定您只需向其传递1、2或3,任何“未初始化”的警告在这种情况下都是虚假的,并且从而降低编译器警告生成的信噪比。
我同意如果编译器能够针对此类情况提供更好的诊断信息会很好,但这并不容易。对于可能出现误报的警告来说,始终需要在代码中权衡修复警告和未能捕获漏洞之间进行微妙平衡。

这正是在gcc手册中关于-Wuninitialized选项的例子。正如手册所说,出于那个原因,“这些警告是可选的”。显然,这是错误的,没有任何选项可以获得该警告。 - clandau
2
在这些情况下,我很乐意接受假阳性,也就是虚假警告,尤其是当我明确打开了-Wuninitialized-Wall时。这比目前所有的假阴性要好得多(即应该有警告但没有警告)。可以说,如果编译器无法可靠地解决它,那么人类也无法可靠地解决它,因此变量应该有一个初始化程序。 - Craig McQueen

1

uninitializedTest.c:

#include <stdio.h>
#include <stdlib.h>


int main(void)
{
   int result;
   if(rand())
      result = 1;

   printf("%d\n", result);

   return 0;
}

这里有几个测试运行:

$ avr-gcc -Os -Wuninitialized -o uninitializedTest uninitializedTest.c 
$ gcc-4.2 -Os -Wuninitialized -o uninitializedTest uninitializedTest.c 
$ gcc-4.0 -Os -Wuninitialized -o uninitializedTest uninitializedTest.c 
uninitializedTest.c: In function ‘main’:
uninitializedTest.c:32: warning: ‘result’ may be used uninitialized in this function

$ gcc-4.2 -Os -Wmaybe-uninitialized -o uninitializedTest uninitializedTest.c 
cc1: error: unrecognized command line option "-Wmaybe-uninitialized"
$ gcc-4.2 -Os -Wuninitialized -o uninitializedTest uninitializedTest.c 
$ gcc-mp-4.8 -Os -Wuninitialized -o uninitializedTest uninitializedTest.c 
$ gcc-mp-4.8 -Os -Wmaybe-uninitialized -o uninitializedTest uninitializedTest.c 

avr-gcc是4.8版本(也适用于avr-gcc-4.4.5),

gcc-4.0似乎有识别该问题的能力。

$ gcc-4.0 -v
Configured with: /var/tmp/gcc/gcc-5493~1/src/configure --disable-checking -enable-werror --prefix=/usr --mandir=/share/man --enable-languages=c,objc,c++,obj-c++ --program-transform-name=/^[cg][^.-]*$/s/$/-4.0/ --with-gxx-include-dir=/include/c++/4.0.0 --with-slibdir=/usr/lib --build=i686-apple-darwin9 --program-prefix= --host=powerpc-apple-darwin9 --target=powerpc-apple-darwin9
Thread model: posix
gcc version 4.0.1 (Apple Inc. build 5493)

那么...这是苹果特有的添加,他们持有许可证吗?或者这就是为什么苹果一直在远离开源的原因...难道他们修复了一个十年前的漏洞,但gcc社区没有接受它吗?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接