为什么gcc和clang都没有发出任何警告?

时间:2017-07-27 09:55:18

标签: c++ c gcc clang compiler-warnings

假设我们有这样的代码:

int check(){
    int x = 5;

    ++x; /* line 1.*/

    return 0;
}

int main(){
    return check();
}

如果注释掉了line 1并且启动了编译器并启用了所有警告,则会发出:

warning: unused variable ‘x’ [-Wunused-variable]

但是,如果我们取消评论line 1,即增加x,则不会发出警告。

为什么?增加变量并没有真正使用它。

对于,GCC和Clang都会发生这种情况。

7 个答案:

答案 0 :(得分:6)

x++与作业x = x+1;相同。当您分配某些内容时,您可能无法跳过使用它。结果是被丢弃。

此外,来自online gcc manual,关于-Wunused-variable选项

  

除了声明之外,本地或静态变量未使用时发出警告。

因此,当您对x++;发表评论时,它会满足生成和发出警告消息的条件。当您取消注释时,编译器可以看到该用法(此特定“用法”的“有用性”值得怀疑,但是,它仍然是一种用法)并且没有警告。

答案 1 :(得分:4)

使用preincrement,您将再次递增并将值再次赋值给变量。它就像:

x=x+1

正如gcc文档所说:

  

-Wunused变量:       除了声明之外,每当局部或静态变量未被使用时发出警告。

如果您评论该行,则表示您没有使用您声明它的行旁边的变量

答案 2 :(得分:3)

  

增加变量并没有真正使用它。

当然这是使用它。它对存储的对象进行读取和写入访问。此操作对您的简单玩具代码中的没有任何影响,优化程序可能会注意到并完全删除变量。但警告背后的逻辑要简单得多:警告 iff 从不使用变量。

这实际上有益于你可以在有意义的情况下使这个警告沉默:

void someCallback(void *data)
{
    (void)data; // <- this "uses" data

    // [...] handler code that doesn't need data
}

答案 3 :(得分:1)

  

为什么?增加变量并没有真正使用它。

是的,它确实在使用它。至少从语言的角度来看。我希望优化器删除变量的所有跟踪。

当然,特定用途对程序的其余部分没有影响,因此变量确实是多余的。我同意在这种情况下警告会有所帮助。但是,你提到的不是未使用警告的目的。

然而,考虑分析特定变量是否对程序的执行有任何影响是非常困难的。必须有一个点,编译器停止检查变量是否实际有用。看来,您测试的编译器生成警告的阶段仅检查变量是否至少使用一次。那曾经是增量操作。

答案 4 :(得分:1)

我认为对于使用&#39;这个词有一种误解。以及编译器对此的意义。如果你有一个++i,你不仅要访问变量,你甚至要修改它,而AFAIK就算是&#39;使用&#39;。

编译器可以识别的内容有限制&#39;如何&#39;正在使用变量,如果这些陈述有意义。事实上,clang和gcc都会尝试删除不必要的语句,具体取决于-O - 标志(有时过于激进)。但这些优化没有警告就会发生。

检测一个从未被访问或使用过的变量(没有进一步声明提及该变量)相当容易。

答案 5 :(得分:1)

我同意你的意见,可能会对此产生警告。我认为它并没有产生警告,因为编译器的开发人员并没有为处理这种情况而烦恼(尚未)。也许是因为它太复杂了。但也许他们将来会这样做(提示:你可以建议他们这个警告)。

编译器收到越来越多的警告。例如,GCC中有-Wunused-but-set-variable(这是一个&#34;新的&#34;警告,2011年在GCC 4.6中引入),警告说:

void fn() {
    int a;
    a = 2;
}

所以期待这也发出警告是完全没问题的(这里没有任何不同,代码也没有任何用处):

void fn() {
    int a = 1;
    a++;
}

也许他们可以添加新警告,例如-Wmeaningless-variable

答案 6 :(得分:0)

根据C标准 ISO / IEC 9899:201x ,总是执行表达式评估以允许产生表达式的副作用,除非编译器不能足够确定除去它,程序执行不会改变。

  

5.1.2.3程序执行

     

在抽象机器中,所有表达式都按语义指定的方式进行计算。实际实现不需要评估表达式的一部分,如果它可以推断出它的值没有被使用并且没有产生所需的副作用(包括由调用函数或访问易失性对象引起的任何副作用)。

删除行

++x;

编译器可以推断出局部变量x已定义并初始化,但未使用。

添加时,表达式本身可以被视为void表达式,必须评估副作用,如下所述:

  

6.8.3表达式和空语句

     

表达式语句中的表达式被计算为其副作用的void表达式。

另一方面,删除相对于未使用变量的编译器警告,将表达式转换为void非常常见。即对于函数中未使用的参数,您可以编写:

int MyFunc(int unused)
{
   (void)unused;
   ...
   return a;
}

在这种情况下,我们有一个引用符号unused的void表达式。