当我将字符串文字分配给非常量指针时,为什么我的 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]
以下代码可以很好地编译,例如 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-constchar*
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 usingconst
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]