考虑此C代码:
void foo(char *);
void bar(void) {
foo("");
}
[当我使用带有GCC或Clang的-pedantic -Wall -Wextra
或带有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 *);
| ^~~~~~
叮当给了我这个:
<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 *);
^
在我看来很多事情都不对:
-Wdiscarded-qualifiers
,但是如果我通过它而不是-Wwrite-strings
,则不会收到该警告。-Weverything
的意思是“实际上编译器知道的每一个警告”,但这似乎与此矛盾。这里发生了什么?为什么此特定警告似乎如此错误?
在C中,字符串文字在const
之前存在。因此C字符串文字不是const限定的(尽管尝试写入它们的结果未由C标准定义)。如果将字符串文字设为const限定,则许多旧版软件会因类型错误而损坏。 C委员会认为此更改不值得。
尽管-Wwrite-strings
,但开关-W
并不是真正的警告开关。它将正在编译的语言更改为非标准C,其中字符串文字是const限定的。这解释了为什么在将字符串文字分配给-Wdiscarded-qualifier
时GCC会显示char *
,并且还解释了为什么单独-Wdiscarded-qualifier
不会触发这些警告的原因-因为没有-Wwrite-strings
,所以字符串文字不是const限定的,因此该分配不会丢弃任何限定词。
大概是Clang的-Weverything
不包含-Wwrite-strings
,因为如上所述,它并不是真正的警告选项,并且因为它将语言更改为非标准C。
这似乎是一个非常重要的警告,不仅默认情况下处于关闭状态,而且即使人们实践中采用了大多数方式来使警告处于关闭状态,也要处于关闭状态。
嗯,gcc的作者不同意您的意见,他们在手册中解释了原因:
-Wwrite-strings
[编译C时,将字符串常量的类型设置为
const char[length]
,以便将一个地址复制到非const
char *
指针中会产生警告。这些警告可以帮助您在编译时找到可以尝试写入字符串常量的代码,但前提是您非常注意在声明和原型中使用const
。否则,这只是个麻烦。这就是为什么我们没有使-Wall
请求这些警告的原因。
这也解释了您的第二个问题:
[GCC的输出引用-Wdiscarded-qualifiers,但是如果我通过它而不是-Wwrite-strings,则不会收到该警告。
由于没有-Wwrite-strings
,字符串常量仅具有类型char[]
,因此没有任何限定符被丢弃。
(这也解释了为什么消息中提到-Wdiscarded-qualifiers
而不是-Wwrite-strings
;问题确实是丢弃的限定词,并且该警告代码没有回溯告诉您,唯一的原因是第一名是因为有一个不同的选择。那将非常复杂。)
我以为-万物的意思是“实际上编译器知道的每一个警告”,但这似乎与此矛盾。
是的,这就是clang手册声称的意思:
除了传统的-W标志,还可以通过传递-Weverything来启用所有诊断。这与-Werror一起正常工作,并且还包含-pedantic的警告。
确实,这已被记录为错误:https://bugs.llvm.org/show_bug.cgi?id=18801。显然-Weverything
确实在clang 3.4及更早版本中启用了此警告,但存在回归(或未记录的有意更改)。