#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; }
编译器会发出警告:
$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]
const char search[] = "é";
同样的编译没有任何警告:为什么?
注意:如果我交换Σ和é,我的行为完全相同:如果搜索中的字符不在str中,我会收到警告.
解决方法
这是我编写的一个小程序来说明问题.
#include <stdio.h> #include <string.h> int main(void) { const char message[] = "hello"; #ifdef ASCII_ONLY const char search_for[] = "h"; #else const char search_for[] = "Ʃ"; #endif char *non_const_message = strstr(message,search_for); if (non_const_message == NULL) { puts("non_const_message == NULL"); } else { puts(non_const_message); } }
当我用它编译时
gcc -DASCII_ONLY -std=c99 -pedantic-errors c.c -o c
(在Linux Mint 17上使用gcc 4.8.2),它编译时没有诊断消息,并且打印结果程序
hello
(我使用-pedantic-errors,因为这会导致gcc(尝试)成为符合标准的ISO C编译器.)
当我删除-DASCII_ONLY选项时,我收到编译时错误消息:
c.c: In function ‘main’: c.c:11:31: error: initialization discards ‘const’ qualifier from pointer target type char *non_const_message = strstr(message,search_for);
strstr函数返回char *类型的结果,而不是const char *.它需要两个const char *参数,并且使用正确的搜索字符串,它可以返回其第一个参数的值.这意味着它可以静默地丢弃其参数的常量.我认为这是C标准库中的一个缺陷,但我们可能会坚持使用它.如果他们想要保持一致,那么符合C实现就没有选择“修复”这个缺陷;他们可以警告strstr的危险使用,但他们不能拒绝其他合法的代码.
(通过将strstr分成两个具有不同名称的函数,可以避免这个缺陷,一个采用const char *并返回一个const char *,另一个采用char *并返回一个char *.1989 ANSI C委员会没有’ t抓住机会这样做,要么是因为他们没有想到它,要么是因为他们不想破坏现有代码.C通过拥有两个重载版本的strstr来解决它,这不是C的可能性.)
我的第一个假设是gcc“神奇地”做了类似于C的事情 – 但是仅使用ASCII字符丢弃const的示例不会导致诊断消息.正如我的测试程序所示,问题是通过在字符串文字中使用非ASCII字符(“Σ”而不是“h”)来触发的.
当我使用gcc 4.9.1(我从源代码安装)而不是gcc 4.8.2(我系统上安装的默认版本)时,问题就消失了:
$gcc -DASCII_ONLY -std=c99 -pedantic-errors c.c -o c && ./c hello $gcc -std=c99 -pedantic-errors c.c -o c && ./c c.c: In function ‘main’: c.c:11:31: error: initialization discards ‘const’ qualifier from pointer target type char *non_const_message = strstr(message,search_for); ^ $gcc-4.9.1 -DASCII_ONLY -std=c99 -pedantic-errors c.c -o c && ./c hello $gcc-4.9.1 -std=c99 -pedantic-errors c.c -o c && ./c non_const_message == NULL $
我没有进一步追踪这个bug,但你可能会在4.8.2和4.9.1之间修复的gcc错误列表中找到它.
对于问题中的代码,您可以通过将pos定义为const char *而不是char *来避免此问题.无论如何它应该是const char *,因为它指向一个被定义为const的对象.