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

ano*_*nol 18 c string compiler-warnings string-literals language-lawyer

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

int main() {
  char *s = "immutable";
  s[0] = 'a';
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

尽管如此,似乎绝对没有办法告诉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;
}
Run Code Online (Sandbox Code Playgroud)

此外,这将有助于const在C编程中强制实施一些文化.

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

Sou*_*osh 16

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

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

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

相关阅读:对于任何感兴趣的人,请参阅为什么C字符串文字是只读的?

也就是说,我不太确定这是否是一个不错的选择,但gcc-Wwrite-strings选择权.

引用在线手册,

-Wwrite-strings

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

因此,它使用后门方式产生警告.

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

参考:C11,章节

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

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

这是参考C11章节§6.7.3完成的

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

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

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

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


小智 14

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

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

  • @ user694733将字符串文字的地址分配给`const char(*)[]`变量的代码将被静默接受(除非它自上次检查后已被更改),但在标准C中,没有隐式转换. char(*)[]`到`const char(*)[]`,所以这需要诊断. (4认同)