为什么 C 编译器很难收到 -Wwrite-strings 警告?

Jos*_*ica 2 c gcc clang compiler-warnings gcc-warning

考虑这个 C 代码:

void foo(char *);

void bar(void) {
    foo("");
}
Run Code Online (Sandbox Code Playgroud)

-pedantic -Wall -Wextra当我使用 GCC 或 Clang 或Clang编译该文件时-Weverything,它会编译而不会给出任何相关警告。如果我添加-Wwrite-strings,那么 GCC 会给我这个:

<source>:4:9: warning: passing argument 1 of 'foo' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
    4 |     foo("");
      |         ^~
<source>:1:10: note: expected 'char *' but argument is of type 'const char *'
    1 | void foo(char *);
      |          ^~~~~~
Run Code Online (Sandbox Code Playgroud)

clang 给了我这个:

<source>:4:9: warning: passing 'const char [1]' to parameter of type 'char *' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers]
    foo("");
        ^~
<source>:1:16: note: passing argument to parameter here
void foo(char *);
               ^
Run Code Online (Sandbox Code Playgroud)

对我来说,很多事情似乎都是错误的:

  • 这似乎是一个非常重要的警告,不仅默认情况下要关闭,而且即使人们在实践中启用警告的大多数方式也是如此。
  • GCC 的输出引用-Wdiscarded-qualifiers,但如果我通过它而不是-Wwrite-strings,我不会收到该警告。
  • 我认为-Weverything这意味着“实际上编译器知道的每一个警告”,但这似乎与此相矛盾。
  • 如果我将相同的代码编译为 C++ 而不是 C,那么 GCC 和 Clang 都会给出我想要的警告,而根本不需要任何编译器标志。我不确定为什么这里的语言之间存在差异,因为据我所知,如果您实际写入字符串文字,C 和 C++ 都有未定义的行为。

这里发生了什么?为什么这个特定的警告看起来如此有问题?

Eri*_*hil 5

在 C 语言中,字符串文字早于存在const。因此,C 字符串文字不是 const 限定的(尽管尝试写入它们的结果不是由 C 标准定义的)。如果字符串文字被设置为 const 限定,许多旧软件将由于类型错误而崩溃。C 委员会认为这一改变不值得。

-Wwrite-strings尽管 ,该开关并不是真正的警告开关-W。它将正在编译的语言更改为非标准 C,其中字符串文字是 const 限定的。这解释了为什么-Wdiscarded-qualifier当将字符串文字分配给 a 时GCC 显示 a char *,并且还解释了为什么-Wdiscarded-qualifier单独不会触发这些警告 - 因为没有-Wwrite-strings,字符串文字不是 const 限定的,因此分配不会丢弃任何限定符。

据推测,Clang-Weverything不包括-Wwrite-strings,因为如上所述,它并不是真正的警告选项,而且因为它将语言更改为非标准 C。