【问题标题】:Why don't C compilers warn about incompatible types with literal strings? [duplicate]为什么 C 编译器不警告与文字字符串不兼容的类型? [复制]
【发布时间】:2017-09-06 07:10:15
【问题描述】:

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

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-culture。

为什么故意忽略这种情况?该标准是否积极禁止在这种情况下发出诊断信息,还是主要是为了向后兼容(现在尝试强制执行它们会产生太多警告)?

【问题讨论】:

  • 默认情况下它没有打开,因为遗憾的是仍然有大量以非常量正确方式编写的遗留代码。有些甚至早于将 const 添加到 C 中。
  • 默认启用此类警告会导致警报疲劳。让编译器对此发出警告会很好,但是有太多旧代码会绊倒它,同时仍然是正确的代码。

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


【解决方案1】:

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

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

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

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

也就是说,我不太确定这是否是一个好的选项,但gcc-Wwrite-strings 选项。

引用online manual

-Wwrite-strings

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

因此,它使用 backdoor 方式产生警告。

根据定义,C 字符串文字(即字符串文字)是带有空终止符的char 数组。该标准不要求它们必须是const 合格的。

参考:C11,章节

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

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

参考C11,第 6.7.3 章

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

因此,这里编译器会为将const 限定类型分配给非const 限定类型产生警告。

与为什么使用-Wall -Wextra -pedantic -std=c11 不会产生此警告有关,是再次引用引用

[...] 这些警告可以帮助您在编译时找到可以尝试写入字符串常量的代码,但前提是您必须非常小心地在声明和原型中使用 const。否则,这只是一个麻烦。这就是我们没有让-Wall 请求这些警告的原因。

【讨论】:

  • “否则,那就麻烦了。” 听起来写这本手册的人是在为自己的代码找借口:P。
  • 嗯...也许不是? const-to non-const 既不允许也不隐含,需要进行诊断。使一侧强制 const 合格可能会破坏一些有效的代码,你知道吗?
  • 是的,您不能将此选项与旧代码一起使用。但是任何新代码都应该努力做到 const 正确。我只是觉得手动听起来过于防御性很有趣。
  • 那句话是20多年前写的。那时,我们对让 C 程序员改进他们草率的代码的可能性没有那么乐观。如果我现在正在修改这本手册,我会说一些更积极的话,也许“如果你正在从头开始编写一个新程序,我们建议你使用这个选项。但是,为现有程序启用它将是一项大量工作,而且不是可能会发现很多错误。”
【解决方案2】:

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

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

【讨论】:

  • “在极少数情况下,无效代码会被静默接受。” 这会怎样?如果-Wwrite-strings 让错误检查更加严格,这应该是不可能的。
  • @user694733 将字符串文字的地址分配给const char (*)[] 变量的代码将被静默接受(除非自从我上次检查以来它已被更改),但在标准 C 中,没有隐式转换从char (*)[]const char (*)[],所以这需要诊断。
  • 不知道为什么有人会接受字符串文字的地址,但我想你是对的。我仍然认为关于正常情况的警告char * a = "x";,超过了可能的问题。
猜你喜欢
  • 2015-03-10
  • 1970-01-01
  • 1970-01-01
  • 2011-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-16
  • 2012-07-22
相关资源
最近更新 更多