为什么const限定符警告依赖于变量内容,而不是类型?

Coc*_*nop 6 c string gcc

gcc用以下代码发现了一个奇怪的行为:

#include <stdio.h>
#include <string.h>

int main(void)
{
    const char str[] = "This string contains é which is a multi-byte character";
    const char search[] = "?";
    char * pos = strstr(str, search);
    printf("%s\n", pos);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

编译器会发出警告:

$ gcc toto.c -std=c99
toto.c: In function ‘main’:
toto.c:8:18: warning: initialization discards ‘const’ qualifier
from pointer target type [enabled by default]
Run Code Online (Sandbox Code Playgroud)

但如果我改变以下内容search:

const char search[] = "é";
Run Code Online (Sandbox Code Playgroud)

同样的编译没有任何警告:为什么?

注意:如果我交换?,我有完全相同的行为é:如果字符in search不存在str,我会收到警告.

Sam*_*hik 7

这里有几件事情正在发生.

gcc的头文件指示gcc使用其内置的,优化的strstr(),编译器知道它是什么.纯粹从语言的角度来看,strstr()只是一些库函数,理论上,编译器不知道.但是,gcc实际上知道它是什么.

gcc的优化版本strstr()如果字符串参数是a char *,则strstr()返回a char *; 但如果字符串参数是a const char *,则strstr()返回a const char *,这是有道理的.

因此,在您的情况下,strstr()返回a const char *,这会导致明显的错误,分配给非const char *.

似乎正在发生的事情是,在你的问题的第二部分,gcc找出字符串存在,并优化整个事情; 但在这种情况下,它也应该导致const char *char *转换,并发出警告.不确定这个.

  • 是的,gcc真是太棒了. (2认同)

Kei*_*son 3

这似乎是 gcc 中的一个错误,已在以后的版本中得到纠正。

\n\n

这是我写的一个小程序来说明这个问题。

\n\n
#include <stdio.h>\n#include <string.h>\n\nint main(void) {\n    const char message[] = "hello";\n#ifdef ASCII_ONLY\n    const char search_for[] = "h";\n#else\n    const char search_for[] = "\xc6\xa9";\n#endif\n    char *non_const_message = strstr(message, search_for);\n    if (non_const_message == NULL) {\n        puts("non_const_message == NULL");\n    }\n    else {\n        puts(non_const_message);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

当我用以下命令编译它时

\n\n
gcc -DASCII_ONLY -std=c99 -pedantic-errors c.c -o c\n
Run Code Online (Sandbox Code Playgroud)\n\n

(在 Linux Mint 17 上使用 gcc 4.8.2),它编译时没有诊断消息,并且生成的程序打印

\n\n
hello\n
Run Code Online (Sandbox Code Playgroud)\n\n

(我使用它-pedantic-errors是因为这会导致 gcc(尝试)成为符合 ISO C 的编译器。)

\n\n

当我删除该-DASCII_ONLY选项时,我收到一条编译时错误消息:

\n\n
c.c: In function \xe2\x80\x98main\xe2\x80\x99:\nc.c:11:31: error: initialization discards \xe2\x80\x98const\xe2\x80\x99 qualifier from pointer target type\n     char *non_const_message = strstr(message, search_for);\n
Run Code Online (Sandbox Code Playgroud)\n\n

strstr函数返回类型为 的结果char*而不是 const char*。它需要两个const char*参数,并且使用正确的搜索字符串,它可以返回第一个参数的值。这意味着它可以默默地放弃const其论点的本质。我认为这是 C 标准库中的一个缺陷,但我们可能会坚持下去。如果一致的 C 实现想要保持一致,则无法选择“修复”此缺陷;他们可以警告危险的使用strstr,但不能拒绝其他合法代码。

\n\n

strstr(这个缺陷可以通过分成两个不同名称的函数来避免,一个函数接受 aconst char*并返回 a const char*,另一个接受 achar*并返回 a char*。1989 年 ANSI C 委员会没有利用这个机会这样做,因为他们没有想到这一点,或者因为他们不想破坏现有代码。C++ 通过使用 的两个重载版本来解决这个问题strstr,而这对于 C 来说是不可能的。)

\n\n

我的第一个假设是 gcc 正在“神奇地”做一些与 C++ 类似的事情——但是const仅使用 ASCII 字符丢弃的示例不会导致诊断消息。"\xc6\xa9"正如我的测试程序所示,问题是由在字符串文字(而不是)中使用非 ASCII 字符触发的"h"

\n\n

当我使用 gcc 4.9.1(我从源代码安装)而不是 gcc 4.8.2(我的系统上安装的默认版本)时,问题就消失了:

\n\n
$ gcc -DASCII_ONLY -std=c99 -pedantic-errors c.c -o c && ./c\nhello\n$ gcc -std=c99 -pedantic-errors c.c -o c && ./c\nc.c: In function \xe2\x80\x98main\xe2\x80\x99:\nc.c:11:31: error: initialization discards \xe2\x80\x98const\xe2\x80\x99 qualifier from pointer target type\n     char *non_const_message = strstr(message, search_for);\n                               ^\n$ gcc-4.9.1 -DASCII_ONLY -std=c99 -pedantic-errors c.c -o c && ./c\nhello\n$ gcc-4.9.1 -std=c99 -pedantic-errors c.c -o c && ./c\nnon_const_message == NULL\n$ \n
Run Code Online (Sandbox Code Playgroud)\n\n

我还没有进一步追踪该错误,但您可能可以在 4.8.2 和 4.9.1 之间修复的 gcc 错误列表中找到它

\n\n

对于问题中的代码,您可以通过定义posasconst char*而不是 来避免问题char*。无论如何它应该是const char*,因为它指向一个定义为 的对象const

\n