2011-02-06 97 views
7

我使用g ++,警告级别为-Wall -Wextra,并将警告视为错误(-Werror)。g ++编译器中的“未初始化的使用”警告

现在我有时会收到错误“变量可能在此函数中未初始化使用”。

“有时”是指我有两个独立的编译单元,它们都包含相同的头文件。一个编译单元编译没有错误,另一个编译单元给出上述错误。

头文件中的相关代码段如下。由于该功能相当长,我只能复制下面的相关位。

确切的错误是:

“cmpres”可在此功能

使用未初始化的而且我通过以下*标有错误的行。

for (; ;) { 
    int cmpres; // * 
    while (b <= c and (cmpres = cmp(b, pivot)) <= 0) { 
     if (cmpres == 0) 
      ::std::iter_swap(a++, b); 
     ++b; 
    } 
    while (c >= b and (cmpres = cmp(c, pivot)) >= 0) { 
     if (cmpres == 0) 
      ::std::iter_swap(d--, c); 
     --c; 
    } 
    if (b > c) break; 
    ::std::iter_swap(b++, c--); 
} 

cmp是一个算符,它有两个指针xy和返回-1,0或1,如果*x < *y*x == *y*x > *y分别的其它变量的指针到相同的阵列。)

这段代码是更大功能的一部分,但变量cmpres用于其他地方。因此我不明白为什么会产生这个警告。此外,编译器显然理解cmpres永远不会被读取未初始化(或至少,它并不总是警告,见上文)。

现在我有两个问题:

  1. 为什么不一致的行为?此警告是否由启发式生成? (这似乎是合理的,因为发出此警告需要进行控制流分析,这在一般情况下是NP难以处理的,并且不能总是执行。)

  2. 为什么警告?我的代码不安全?我已经开始欣赏这个特别的警告,因为它使我很难在其他情况下检测到错误 - 所以这个有效的警告,至少有时候。这里有效吗?

+4

只要说`int cmpres = 0;`在for(;;)`循环之外并且继续你的生活;) – fredoverflow 2011-02-06 12:28:26

+0

@Fred我的问题不是让警告消失,而是看警告实际上是*正确*,我只是太愚蠢,不能注意到我的代码中存在错误。 – 2011-02-06 12:40:27

回答

12

诊断未初始化变量且没有错误否定或肯定结果的算法必须(作为子例程)包含解决Halting Problem的算法。这意味着没有这样的算法。这是不可能让计算机100%的时间获得这个权利。

我不知道GCC的未初始化变量分析是如何工作的,但我确实知道它对早期优化过程对代码做了什么非常敏感。所以我一点都不感到惊讶,你有时只会得到误报。它区分情况下,它是从情况下,它不能是某些特定的 -

int foo() { int a; return a; } 

生产“警告:‘A’是用在这个函数初始化”(重点煤矿)。

编辑:我发现,如果GCC的最新版本(4.3及更高版本)失败诊断未初始化的变量的情况下:

int foo(int x) 
{ 
    int a; 
    return x ? a : 0; 
} 

早期优化注意到,如果x为非零值,函数的行为因此他们假设x必须为零并用“return 0;”代替函数的整个主体这发生在产生使用未初始化警告的过程之前,因此没有诊断。有关血淋淋的细节见GCC bug 18501

我将这部分内容展示出来,证明生产级编译器可以通过两种方式获得未初始化变量的诊断,部分原因是这是一个很好的例子,未定义的行为可以在执行时反向传播。没有关于测试x的未定义,但由于代码控制依赖于x具有未定义的行为,因此允许编译器假定控制相关性永远不会被满足并放弃测试。

2

该代码是正确的,但编译器无法识别该变量从未在没有初始化的情况下使用。

1

我认为这可能是一个启发式错误 - 这就是“可能”的目的。我怀疑没有太多循环条件看起来很像。该代码不是不安全的,因为在所有控制路径中,cmpres都是在使用前分配的。不过,我当然不会觉得它首先初始化它是错误的。

但是,您可以在这里进行某种可变阴影处理。这是我能想到的仅有的两个翻译单位之一给出错误的唯一解释。

+0

重点是,我会假设*如果*这是一种启发式,编译器并不总是注意到这一点,那么警告实际上是合理的(并指向逻辑错误),编译器并不总是注意到它... – 2011-02-06 12:41:06

3

本周有关于这些启发式的clang dev-mailing list的有趣讨论。

底线是:这实际上是相当困难的诊断未初始化值没有得到指数行为......

显然(从讨论),GCC使用谓词基地的做法,但由于你的经验似乎它并不总是足够的。

我怀疑这与作业在条件内混合(以及在短路操作员之后......)有关。你有没有试过?

我认为gcc和clang的人都会对这个例子非常感兴趣,因为它是C或C++中相对常见的做法,因此可以从一些调优中受益。

+0

是不是GCC和叮当声SSA编译器? SSA编译器根据其使用的代码路径将变量分成多个变量。在这种情况下,SSA编译器应该在两个分支中为两个分割“cmpres”。在这两个分支中,都有一个跟在分配之后的阅读。 – MSalters 2011-02-08 08:36:19

相关问题