为什么我的C编译器在我把一个字符串赋值给一个非常量指针时不发出警告?

xoefb8l8  于 2023-05-22  发布在  其他
关注(0)|答案(3)|浏览(150)

下面的代码使用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"的字节的内存被编译到我的程序的二进制文件中的某个地方,在现代处理器/操作系统上被标记为code rather than data。(This answer证实了这一点--更不用说反汇编中有一行leaq 0x4d(%rip), %rsi ; "123",它将指针传递到相对于指令指针的地址!)
C语言允许这样做只是一个历史产物,来自自我修改代码的时代吗?我注意到我也可以分配void* x = main;,而不会抱怨我丢弃了修饰符。
This answer说:
根据C99的基本原理,委员会中有人希望字符串文字是可修改的,所以标准没有明确禁止它。
我能读到关于这一点的进一步讨论吗?更实际的是,有没有一种方法可以告诉clang和/或gcc标记这样的赋值(即使它们实际上并不被禁止)并发出警告,而不需要编译为C++?

nwnhqdif

nwnhqdif1#

你引用的答案是一个没有引用的观点,坦率地说是胡说八道。它只不过是不破坏大量现有的遗留C代码,希望在现代编译器中保持可编译性。
然而,如果你设置了必要的警告级别或选项,许多编译器会发出警告。在GCC中:
-Wwrite-strings
在编译C时,给予字符串常量指定const char[length]类型,以便将一个的地址复制到非const char*指针中会产生警告。这些警告可以帮助您在编译时找到可能尝试写入字符串常量的代码,但前提是您必须非常小心地在声明和原型中使用const。否则,它只是一个麻烦。这就是为什么我们没有让-Wall请求这些警告的原因。
编译C时,警告从字符串字面量到char * 的转换已被弃用。对于C程序,默认情况下启用此警告。
CLANG也有-Wwrite-strings,其中是-Wwriteable-strings的同义词
-Wwritable-strings
默认情况下启用此诊断。
还控制-Wdeprecated-writable-strings

诊断文本:

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]
cpjpxq1n

cpjpxq1n2#

与C相反,在C中,字符串字面量具有非常量字符数组的类型。
然而,根据C标准,任何修改字符串文字的尝试都会导致未定义的行为。
历史上,C语言没有限定符const。const这个限定符最早出现在C
中。因此,为了向后兼容,C中的字符串字面量具有非常量字符数组的类型。

w8f9ii69

w8f9ii693#

你有-Wwrite-strings
在编译C时,给予字符串常量类型const char[length],以便将一个的地址复制到非const char * 指针中会产生警告。这些警告可以帮助您在编译时找到可能尝试写入字符串常量的代码,但前提是您必须非常小心地在声明和原型中使用const。否则,它只是一个麻烦。这就是为什么我们没有提出-墙要求这些警告。

相关问题