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

Why gcc and clang both don't emit any warning?

假设我们有这样的代码:

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 中都发生了 and

是的。

x++x = x+1;相同,赋值。当你分配给某物时,你可能无法 skip 使用它。结果是丢弃。

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

Warn whenever a local or static variable is unused aside from its declaration.

因此,当您评论 x++; 时,它满足生成并发出警告消息的条件。当您取消注释时,编译器可以看到该用法(这个特定 "usage" 的 "usefulness" 是有问题的,但是,它仍然是一个用法)并且没有警告。

使用预增量,您将递增并再次分配 值给变量。就像:

x=x+1

正如 gcc 文档所说:

-Wunused-variable: Warn whenever a local or static variable is unused aside from its declaration.

如果您注释掉该行,则您没有在声明它的行旁边使用该变量

increasing variable not really using it.

确定这是使用它。它正在对存储的对象进行读取和写入访问。此操作在您的简单玩具代码 中 没有任何影响,优化器可能会注意到这一点并完全删除该变量。但警告背后的逻辑要简单得多:warn iff​​ the variable is never used.

这实际上有一个好处,您可以在有意义的情况下使该警告静音:

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

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

Why is that? increasing variable not really using it.

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

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

但是,考虑到分析一个特定的变量是否对程序的执行有任何影响,一般来说是相当困难的。必须有一个点,编译器会停止检查变量是否真的有用。似乎生成您测试的编译器警告的阶段仅检查变量是否至少使用一次。那个曾经是自增操作

我认为人们对 'using' 这个词以及编译器的含义存在误解。当你有一个 ++i 时,你不仅在访问变量,你甚至在修改它,据我所知,这算作 'use'.

对于编译器可以识别为正在使用的 'how' 个变量,以及这些语句是否有意义,存在一些限制。事实上,clang 和 gcc 都会尝试删除不必要的语句,具体取决于 -O 标志(有时过于激进)。但是这些优化是在没有警告的情况下发生的。

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

我同意你的看法,它可能会生成关于此的警告。我认为它不会生成警告,因为编译器的开发人员还没有费心处理这种情况(还)。也许是因为它太复杂了。但也许他们将来会这样做(提示:您可以向他们建议此警告)。

编译器收到越来越多的警告。比如GCC中有-Wunused-but-set-variable(这是一个"new"警告,2011年在GCC 4.6中引入),警告是这样的:

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

所以期望这也会发出警告是完全没问题的(这里没有什么不同,两个代码都没有做任何有用的事情):

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

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

根据 C 标准 ISO/IEC 9899:201x,始终执行表达式求值以允许产生表达式的副作用,除非编译器不能确保删除它不会改变程序执行。

5.1.2.3 Program execution

In the abstract machine, all expressions are evaluated as specified by the semantics. An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object).

删除行时

++x;

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

当你添加它时,表达式本身可以被认为是一个void表达式,必须计算副作用,如:

6.8.3 Expression and null statements

The expression in an expression statement is evaluated as a void expression for its side effects.

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

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

在这种情况下,我们有一个引用符号 unused.

的空表达式