为什么C编译器不会用文字字符串警告不兼容的类型?

时间:2017-09-06 07:10:15

标签: c string language-lawyer compiler-warnings string-literals

以下程序由于未定义的行为(尝试修改字符串文字)而引发系统分段错误:

int main() {
  char *s = "immutable";
  s[0] = 'a';
  return 0;
}

尽管如此,似乎绝对没有办法告诉GCC / Clang即使发出最轻微的警告(-Wall -Wextra -pedantic -std=c11也不做任何事情)。

特别是对于初学者来说,这种情况对于告知有用。即使对于非初学者,在一些稍微不那么明显的情况下,它也会有所帮助:

void f(char *s) {
  s[0] = '0';
}

int main() {
  char *s = "immutable";
  f("literal"); // oops
  f(s); // oops
  return 0;
}

此外,这将有助于在C编程中强制执行一些const文化。

为什么故意忽略此类案件?标准是否主动禁止在这种情况下发出诊断,或者它主要是为了向后兼容(现在试图强制执行它们会产生太多警告)?

2 个答案:

答案 0 :(得分:16)

TL; DR C编译器不会发出警告,因为他们没有“看到”那里的问题。根据定义,C字符串文字是空终止的char数组。它只是声明,

  

[...]如果程序试图修改这样的数组,行为是   未定义。

因此,在编译过程中,编译器不知道char数组应该表现为字符串文字字符串。只有修改的尝试 禁止

相关阅读:如有兴趣,请参阅Why are C string literals read-only?

那就是说,我不确定这是否是选项,但gcc-Wwrite-strings选项。

引用online manual

  

-Wwrite-strings

     

编译C时,为字符串常量提供类型const char[length],以便将一个地址复制到非const char *指针中会产生警告。这些警告可以帮助您在编译时找到可以尝试写入字符串常量的代码,但前提是您在声明和原型中使用const非常小心。否则,这只会​​令人讨厌。这就是为什么我们没有让-Wall请求这些警告。

因此,它使用后门方式生成警告。

根据定义,C 字符串文字(即字符串文字)是具有空终止符的char数组。该标准并未强制要求const合格。

参考:C11,章节

  

在转换阶段7中,将值为零的字节或代码附加到每个多字节   由字符串文字或文字产生的字符序列。多字节字符   然后,序列用于初始化静态存储持续时间和长度的数组   足以包含序列。对于字符串文字,数组元素具有   键入char,并使用多字节字符的各个字节进行初始化   序列。 [....]

使用上述选项使得字符串文字 const合格,因此使用字符串文字作为赋予非const类型指针的RHS会触发警告。

这是参考C11,章节§6.7.3

完成的
  

如果尝试通过use修改使用const限定类型定义的对象   如果是非const限定类型的左值,则行为未定义。 [...]

因此,编译器会在此处生成警告,以便将const限定类型分配给非const限定类型。

与使用-Wall -Wextra -pedantic -std=c11不会产生此警告的原因相关,是,再次引用引用

  

[...]这些警告可以帮助您在编译时找到可以尝试写入字符串常量的代码,但前提是您在声明和原型中使用const非常小心。否则,这只会​​令人讨厌。这就是为什么我们没有让-Wall请求这些警告。

答案 1 :(得分:14)

有一个选项:-Wwrite-strings。它的工作原理是将字符串文字的类型从char[N]更改为const char[N]。此更改与标准C不兼容,将导致有效代码被拒绝,并且在极少数情况下,无效代码将被静默接受。它默认不启用。

不幸的是,由于在C中定义了字符串文字的方式,因此在没有更改语言的情况下为此提供良好的警告非常困难。