C语言 编译器未检测到明显未初始化的变量

tag5nh1u  于 2023-05-06  发布在  其他
关注(0)|答案(4)|浏览(129)

我试过的所有C编译器都无法检测到下面代码片段中未初始化的变量。然而,这里的情况是显而易见的。
不要担心这个代码片段的功能。这不是真实的的代码,为了调查这个问题,我把它剥离了。

BOOL NearEqual (int tauxprecis, int max, int value)
{
  int tauxtrouve;      // Not initialized at this point
  int totaldiff;       // Not initialized at this point

  for (int i = 0; i < max; i++)
  {
    if (2 < totaldiff)  // At this point totaldiff is not initialized
    {
      totaldiff = 2;
      tauxtrouve = value;  // Commenting this line out will produce warning
    }
  }

  return tauxtrouve == tauxprecis ;  // At this point tauxtrouve is potentially
                                     // not initialized.
}

另一方面,如果我注解掉tauxtrouve = value ;,我会得到"local variable 'tauxtrouve' used without having been initialized"警告。
我试了这些编译器:

  • GCC 4.9.2带-Wall -WExtra
  • 启用所有警告的Microsoft Visual C++ 2013
2izufjch

2izufjch1#

这个变量没有初始化的明显性被夸大了。路径分析需要花费时间,而您的编译器供应商要么不想实现该功能,要么认为它会花费您太多的时间--或者您只是没有明确地选择加入。
例如,使用clang

$ clang -Wall -Wextra -c obvious.c 
$ clang -Wall -Wextra --analyze -c obvious.c 
obvious.c:9:11: warning: The right operand of '<' is a garbage value
    if (2 < totaldiff)  // at this point totaldiff is not initialized
          ^ ~~~~~~~~~
obvious.c:16:21: warning: The left operand of '==' is a garbage value
  return tauxtrouve == tauxprecis ;  // at this point tauxtrouve is potentially
         ~~~~~~~~~~ ^
2 warnings generated.

这些简单示例的执行时间差异可以忽略不计。但是想象一下,一个翻译单元有数千行代码,数十个函数,每个函数都有循环和大量嵌套。路径的数量迅速地复合并且变成分析通过循环的第一次迭代是否将在该比较之前发生分配的大负担。
编辑:@Matthieu指出,使用LLVM/clang,由于IR使用的SSA符号,查找use-of-uninitialized值所需的路径分析不会随着嵌套的增加而复合。
它并不像我希望的那样简单,“-S -emit-llvm”,但我发现了他描述的SSA表示法输出。老实说,我对LLVM IR还不够熟悉,但我相信Matthieu的话。
底线是:使用clang--analyze,或者说服某人修复gcc错误。

; Function Attrs: nounwind uwtable
define i32 @NearEqual(i32 %tauxprecis, i32 %max, i32 %value) #0 {
  br label %1

; <label>:1                                       ; preds = %7, %0
  %tauxtrouve.0 = phi i32 [ undef, %0 ], [ %tauxtrouve.1, %7 ]
  %i.0 = phi i32 [ 0, %0 ], [ %8, %7 ]
  %2 = icmp slt i32 %i.0, %max
  br i1 %2, label %3, label %9

; <label>:3                                       ; preds = %1
  %4 = icmp slt i32 2, 2
  br i1 %4, label %5, label %6

; <label>:5                                       ; preds = %3
  br label %6

; <label>:6                                       ; preds = %5, %3
  %tauxtrouve.1 = phi i32 [ %value, %5 ], [ %tauxtrouve.0, %3 ]
  br label %7

; <label>:7                                       ; preds = %6
  %8 = add nsw i32 %i.0, 1
  br label %1

; <label>:9                                       ; preds = %1
  %10 = icmp eq i32 %tauxtrouve.0, %tauxprecis
  %11 = zext i1 %10 to i32
  ret i32 %11
}
wr98u20j

wr98u20j2#

是的,它应该对未初始化的变量发出警告,但它是a GCC bug。这里给出的例子是:

unsigned bmp_iter_set ();
int something (void);

void bitmap_print_value_set (void)
{
    unsigned first;

    for (; bmp_iter_set (); )
    {
        if (!first)
            something ();
        first = 0;
    }
}

并被诊断为-O2 -W -Wall
不幸的是,今年是这个bug的10周年!

smtd7mpg

smtd7mpg3#

此答案仅针对GCC。
经过进一步的调查和评论,有更多的事情比我以前的回答。此代码段有两个未初始化的变量,由于不同的原因,它们中的每一个都未被检测到。
首先,-Wuninitialized选项的GCC文档说:
由于这些警告取决于优化,因此存在警告的确切变量或元素取决于所使用的GCC的精确优化选项和版本。
以前版本的GCC手册对此有更明确的表述。以下是GCC 3.3.6手册的摘录:
只有在优化编译时才可能出现这些警告,因为它们需要仅在优化时计算的数据流信息。如果不指定-O,就不会收到这些警告。
参见GCC - no warning about an uninitialized array with -O0
似乎当前版本在没有-O的未初始化变量的情况下可能会给予一些警告,但使用它仍然可以获得更好的结果。
如果我使用gcc -std=c99 -Wall -O编译你的例子,我会得到:

foo.c: In function ‘NearEqual’:
foo.c:15:21: warning: ‘tauxtrouve’ is used uninitialized in this function [-Wuninitialized]
   return tauxtrouve == tauxprecis ;  // at this point tauxtrouve is potentially
                     ^

(Note这是GCC 4.8.2,因为我没有安装4.9.x,但原理应该是一样的。
因此,它检测到tauxtrouve未初始化的事实。
但是,如果我们通过为tauxtrouve添加初始化器(但不为totaldiff添加初始化器)来部分修复代码,那么gcc -std=c99 -Wall -O会接受它而不会发出任何警告。这似乎是haccks's answer中引用的“bug”的一个示例。
这是否真的应该被认为是一个bug还存在一些问题:GCC并不承诺捕获未初始化变量的每个可能示例。事实上,它不能以完美的精度这样做,因为这是halting problem。所以像这样的警告在正常工作时是有帮助的,但是没有警告并不能证明你的代码中没有未初始化的变量!它们真的不能代替仔细检查您自己的代码。
bug report linked by haccks中,有很多关于bug是否可以修复的讨论,或者试图检测这个特定的构造是否会导致其他正确代码的不可接受的误报率。

70gysomp

70gysomp4#

Michael,我不知道你在哪个版本的Visual Studio 2013上尝试过这个,但它肯定已经过时了。Visual Studio 2013 Update 4在首次使用totaldiff时正确生成以下错误消息:

error C4700: uninitialized local variable 'totaldiff' used

你应该考虑更新你的工作环境。
顺便说一下,这是我在编辑器中直接看到的:

相关问题