当我将字符串文字分配给非常量指针时,为什么我的 C 编译器没有发出警告?

Why doesn't my C compiler warn when I assign a string literal to a non-const pointer?

以下代码可以很好地编译,例如 Xcode 11.3.1 中的默认设置:

#include <stdio.h>

int main(int argc, const char * argv[]) {
    char* thing = "123";
    thing[2] = '4';
    printf("%s\n", thing);
    return 0;
}

但是,在运行时,代码会在 thing[2] = '4' 上陷入 EXC_BAD_ACCESS。我假设这是因为代表 "123" 的字节的内存被编译到我的程序的二进制文件中的某个地方,在现代 processor/OS 上被标记为 code rather than data. (This answer 确认 - 更不用说有一个 leaq 0x4d(%rip), %rsi ; "123" 反汇编中的行,将指针传递给相对于 指令指针 !)

的地址

自修改代码时代以来,C 允许这样做只是历史产物吗?我注意到我也可以分配 void* x = main; 而不会抱怨我正在丢弃修饰符。

This answer 说:

According to the C99 rationale, there were people in the committee who wanted string literals to be modifiable, so the standard does not explicitly forbid it.

有没有我可以阅读的进一步讨论?更实际的是,有没有办法告诉 clang and/or gcc 标记此类分配(即使它们实际上并未被禁止) 并发出警告, 不编译为 C++?

与 C++ 相反在 C 中,字符串文字具有非常量字符数组的类型。

然而,根据 C 标准,任何修改字符串文字的尝试都会导致未定义的行为。

从历史上看,C 语言没有限定符 const。限定符 const 最早出现在 C++ 中。因此,为了向后兼容,C 中的字符串文字具有非常量字符数组的类型。

你有 -Wwrite-strings:

When compiling C, give string constants the type const char[length] so that copying the address of one into a non-const char * pointer produces a warning. These warnings help you find at compile time code that can try to write into a string constant, but only if you have been very careful about using const in declarations and prototypes. Otherwise, it is just a nuisance. This is why we did not make -Wall request these warnings.

https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/Warning-Options.html

你引用的答案是没有引用的观点,坦率地说是胡说八道。在现代编译器中保持可编译性无非就是不破坏大量现有的遗留 C 代码。

然而,如果您设置了必要的警告级别或选项,许多编译器发出警告。在 GCC 例如:

-Wwrite-strings

When compiling C, give string constants the type const char[length] so that copying the address of one into a non-const char* pointer produces a warning. These warnings help you find at compile time code that can try to write into a string constant, but only if you have been very careful about using const in declarations and prototypes. Otherwise, it is just a nuisance. This is why we did not make -Wall request these warnings.

When compiling C++, warn about the deprecated conversion from string literals to char *. This warning is enabled by default for C++ programs.

CLANG 也有 -Wwrite-strings,其中是 -Wwriteable-strings

的同义词

-Wwritable-strings

This diagnostic is enabled by default.

Also controls -Wdeprecated-writable-strings.

Diagnostic text:

warning: ISO C++11 does not allow conversion from string literal to A

C 编译的诊断文本不同 - 我只是引用手册。

-Wwrite-strings 的 GCC 中:

int main()
{
    char* x = "hello" ;
    return 0;
}

产生:

main.c:3:15: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]    

CLANG 产生:

source_file.c:3:15: warning: initializing 'char *' with an expression of type 'const char [6]' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers]