以下程序由于未定义的行为(尝试修改字符串文字)而引发系统分段错误:
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
文化。
为什么故意忽略此类案件?标准是否主动禁止在这种情况下发出诊断,或者它主要是为了向后兼容(现在试图强制执行它们会产生太多警告)?
答案 0 :(得分:16)
TL; DR C编译器不会发出警告,因为他们没有“看到”那里的问题。根据定义,C字符串文字是空终止的char
数组。它只是声明,
[...]如果程序试图修改这样的数组,行为是 未定义。
因此,在编译过程中,编译器不知道char
数组应该表现为字符串文字或字符串。只有修改的尝试 禁止 。
相关阅读:如有兴趣,请参阅Why are C string literals read-only?
那就是说,我不确定这是否是好选项,但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
如果尝试通过use修改使用const限定类型定义的对象 如果是非const限定类型的左值,则行为未定义。 [...]
因此,编译器会在此处生成警告,以便将const
限定类型分配给非const
限定类型。
与使用-Wall -Wextra -pedantic -std=c11
不会产生此警告的原因相关,是,再次引用引用
[...]这些警告可以帮助您在编译时找到可以尝试写入字符串常量的代码,但前提是您在声明和原型中使用const非常小心。否则,这只会令人讨厌。这就是为什么我们没有让
-Wall
请求这些警告。
答案 1 :(得分:14)
有一个选项:-Wwrite-strings
。它的工作原理是将字符串文字的类型从char[N]
更改为const char[N]
。此更改与标准C不兼容,将导致有效代码被拒绝,并且在极少数情况下,无效代码将被静默接受。它默认不启用。
不幸的是,由于在C中定义了字符串文字的方式,因此在没有更改语言的情况下为此提供良好的警告非常困难。