【问题标题】:"stderr;" as a statement throws no warning“标准错误;”作为声明没有警告
【发布时间】:2020-09-19 03:42:35
【问题描述】:

假设我有这样的源代码:

#include <stdio.h>

FILE *p;

int main(void) {
    p;
}

...它将编译(使用 gcc)而不会出现任何错误或警告——除非我打开 -Wall,在这种情况下将输出 statement with no effect 警告。

但是,如果代码是这样的:

#include <stdio.h>

int main(void) {
    stderr;
}

...无论-Wall如何,都不会显示任何警告。

我错过了什么吗?

gcc 版本 9.3.0

【问题讨论】:

  • stderr 可能不是常规变量,它实际上可能是宏。尝试通过预处理器运行你的第二个程序,看看它创建了什么。
  • stderr 被定义为 extern FILE * 但 extern 似乎没有什么区别。
  • stderr 需要定义为宏。在我的实现(gcc 9.3.0,glibc 2.31)中,宏stderr 扩展为stderrstderr 定义为extern FILE *stderr;。我看到了同样的行为。如果我#undef stderr 我确实会在stderr; 上收到警告。编译器中可能有一些特殊情况的代码。不存在一致性问题,因为该标准在这里不需要诊断。我看到与 clang 相同的行为。
  • 这可能与声明或宏定义来自系统标头的事实有关。我似乎记得 gcc 专门针对警告处理系统标头,前提是用户无法对其中的代码执行任何操作。
  • @NateEldredge 这似乎差不多。我已经尝试过了,它工作得很好。我已将extern FILE *p;#define p p 添加到/usr/include/stdio.h,然后继续没有警告

标签: c gcc stdout stderr gcc-warning


【解决方案1】:

这个程序:

$ cat main.c
#include <stdio.h>

int main(void)
{
    FILE *p;
    42;         // statement with no effect
    p;          // statement with no effect
    stderr;     // statement with no effect
    return 0;
}

可能会引出 3 个statement with no effect 每当它引起任何诊断。但正如你所发现的, 在gcc 的手中,这不是真的。

$ gcc --version
gcc (Ubuntu 9.3.0-10ubuntu2) 9.3.0
...

$ gcc -Wall -c main.c
main.c: In function ‘main’:
main.c:6:5: warning: statement with no effect [-Wunused-value]
    6 |     42;
      |     ^~
main.c:7:5: warning: statement with no effect [-Wunused-value]
    7 |     p;
      |     ^

stderr - 表示FILE *,如p - 具有隐含的免费通行证 评估无效。

众所周知,-Wall真的不会启用所有警告。但是这个免费 通行证可以达到诊断严格的惯常限制:

$ gcc -Wall -Wextra -pedantic -c main.c
main.c: In function ‘main’:
main.c:6:5: warning: statement with no effect [-Wunused-value]
    6 |     42;
      |     ^~
main.c:7:5: warning: statement with no effect [-Wunused-value]
    7 |     p;
      |     ^

我们应该清楚,这个免费通行证是由 identfier 携带的 stderr,与它命名的值不同:-

通过使另一个FILE *等于stderr,它不能转移到另一个FILE *

$ cat main.c; gcc -Wall -c main.c
#include <stdio.h>

int main(void)
{
    FILE *p = stderr;
    42;
    p;
    return 0;
}

main.c: In function ‘main’:
main.c:6:5: warning: statement with no effect [-Wunused-value]
    6 |     42;
      |     ^~
main.c:7:5: warning: statement with no effect [-Wunused-value]
    7 |     p;
      |     ^

FILE * 不喜欢它实际上 stderr,如果我们不将其引用stderr

$ cat main.c; gcc -Wall -c main.c
#include <stdio.h>

int main(void)
{
    FILE **p = &stderr;
    42;
    *p;     // a.k.a `stderr`
    return 0;
}

main.c: In function ‘main’:
main.c:6:5: warning: statement with no effect [-Wunused-value]
    6 |     42;
      |     ^~
main.c:7:5: warning: statement with no effect [-Wunused-value]
    7 |     *p;     // a.k.a `stderr`
      |     ^~

但另一方面,即使stderr 称为stderr, 如果该标识符小于 评估无效的整个上下文:

$ cat main.c; gcc -Wall -c main.c
#include <stdio.h>

int main(void)
{
    stdout;             // Undiagnosed
    stderr;             // Undiagnosed
    stderr, stdout;     // Diagnosed once
    42, stderr;         // Diagnosed twice
    stderr - stdout;    // Diagnosed once
    (stderr);           // Diagnosed once
    return 0;
}

main.c: In function ‘main’:
main.c:7:11: warning: left-hand operand of comma expression has no effect [-Wunused-value]
    7 |     stderr, stdout;     // Diagnosed once
      |           ^
main.c:8:7: warning: left-hand operand of comma expression has no effect [-Wunused-value]
    8 |     42, stderr;         // Diagnosed twice
      |       ^
main.c:8:5: warning: statement with no effect [-Wunused-value]
    8 |     42, stderr;         // Diagnosed twice
      |     ^~
main.c:9:12: warning: statement with no effect [-Wunused-value]
    9 |     stderr - stdout;    // Diagnosed once
      |            ^
main.c:10:5: warning: statement with no effect [-Wunused-value]
   10 |     (stderr);           // Diagnosed once
      |     ^

在这里,我假设stderr 的情况也一样 对于stdout,这是正确的。这是一个值得注意的细节,虽然42, stderr; 被诊断为无效语句,stderr, stdout; 不是。

可以这么说,gcc 在自然和自然方面并不自信 它想要扩展到stderr 的诊断免疫限制,类似地 合格的标识符。这也许是可以理解的,当我们探索 除了与编译器隔离之外,没有人编写的那种代码的后果。

尽管如此,我们还是希望清楚这一诊断的动机 免疫并知道是否可以告诉gcc 撤销它,例如所有的 我在程序中编写的无效语句将被诊断为此类。

第二个分数的答案是肯定的:

$ cat main.c; gcc -Wall -Wsystem-headers -c main.c
#include <stdio.h>

int main(void)
{
    FILE *p;
    42;         // statement with no effect
    p;          // statement with no effect
    stderr;     // statement with no effect
    return 0;
}
main.c: In function ‘main’:
main.c:6:5: warning: statement with no effect [-Wunused-value]
    6 |     42;         // statement with no effect
      |     ^~
main.c:7:5: warning: statement with no effect [-Wunused-value]
    7 |     p;          // statement with no effect
      |     ^
In file included from main.c:1:
main.c:8:5: warning: statement with no effect [-Wunused-value]
    8 |     stderr;     // statement with no effect
      |     ^~~~~~

和:

$ cat main.c; gcc -Wall -Wsystem-headers -c main.c
#include <stdio.h>

int main(void)
{
    stdout;
    stderr;
    stderr, stdout;
    42, stderr;
    stderr - stdout;
    (stderr);
    return 0;
}
In file included from main.c:1:
main.c: In function ‘main’:
main.c:5:5: warning: statement with no effect [-Wunused-value]
    5 |     stdout;
      |     ^~~~~~
main.c:6:5: warning: statement with no effect [-Wunused-value]
    6 |     stderr;
      |     ^~~~~~
main.c:7:11: warning: left-hand operand of comma expression has no effect [-Wunused-value]
    7 |     stderr, stdout;
      |           ^
In file included from main.c:1:
main.c:7:5: warning: statement with no effect [-Wunused-value]
    7 |     stderr, stdout;
      |     ^~~~~~
main.c:8:7: warning: left-hand operand of comma expression has no effect [-Wunused-value]
    8 |     42, stderr;
      |       ^
main.c:8:5: warning: statement with no effect [-Wunused-value]
    8 |     42, stderr;
      |     ^~
main.c:9:12: warning: statement with no effect [-Wunused-value]
    9 |     stderr - stdout;
      |            ^
main.c:10:5: warning: statement with no effect [-Wunused-value]
   10 |     (stderr);
      |     ^

还有documentation of -Wsystem-headers 提供激励理由:

-Wsystem-headers

打印在系统头文件中找到的构造的警告消息。来自的警告 系统标头通常被抑制,假设它们通常不 表明真正的问题,只会使编译器输出更难阅读。 使用这个命令行选项告诉 GCC 从系统头文件发出警告 如果它们出现在用户代码中。 ...

所以,stderrstderr 通过在系统中声明而获得诊断豁免权 标题,&lt;stdio.h&gt;1。默认情况下,系统标头的警告假定为 是假的。

然而,在我们开展业务之前,值得赞赏的是,记录在案的解释 -Wsystem-headers 的影响,以及它的缺席,实际上并不能解释 我们观察到的那些影响。诊断失败

stderr;     // statement with no effect

在我们的第一个程序中没有-Wsystem-headers不是的压制 来自系统标头的警告。是来自main.c的警告的抑制, 其中该声明与以下内容完全一样无效:

p;          // statement with no effect

-Wsystem-headers对那个程序编译的影响不是 GCC 开始从系统标头发出任何先前抑制的警告 好像它发生在用户代码中。它会导致 GCC 发出先前抑制的 用户代码中一直出现的警告。

显然,默认-Wno-system-headers 的实际效果至少包括: 在上下文时抑制某些警告,无论是否在用户代码中

... identifier ...

否则会引发警告的包含声明的identifier 在系统头文件中。手册告诉我们如何阻止这种情况,但只有手势 在解释它。


[1] 文档中的 system header 是什么意思并不明显, 但实验表明文件只是一个系统头 如果它是GCC安装的头文件,则适当的感觉。

【讨论】:

  • TL;DR:这是一个 gcc 错误。一个真实的。
【解决方案2】:

是的......如果你写这样的东西:

0;

#include <math.h>
int main()
{

    cos(2.0*M_PI + 7.0);
}

你会得到相同的结果(什么都不会发生)。原因是语句可以是任何表达式(任何类型),后跟 C 中的分号 ;。表达式值被删除,并继续执行下一条语句(在您的情况下没有)。在我的示例中,表达式的 0 值或字符串文字 "Hello world" 只是被编译器删除,结果为空。

【讨论】:

  • OP' 理解为什么 -Wall 会针对 p 引发警告 statement with no effect。他们想知道为什么stderr。正确答案在 cmets 中得出。
  • 可能与printf() 相同,这是一个返回写入字符数的函数。或者为什么如果你定义一个函数printf gcc 认为它是一个内置函数,而 C 从来没有内置函数。
  • @LuisColorado 0; 也将通过使用 -Wall 得到警告。只是为了澄清我的意思
  • 是的,但是printf() 不会被警告,并且本身它只是一个简单的表达式,它返回实际写入的字符数。所以基本上,它等价于表达式0,或26。编译器识别它是因为它在警告消息中声称,它是一个 builtin 函数(printf 从未有过的东西)
  • @LuisColorado 不管我是否丢弃printf() 的输出——它确实有效果。因此没有警告
猜你喜欢
  • 1970-01-01
  • 2015-08-21
  • 1970-01-01
  • 2019-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-07
  • 1970-01-01
相关资源
最近更新 更多