首页 > 解决方案 > 当我将字符串文字分配给非常量指针时,为什么我的 C 编译器不发出警告?

问题描述

例如,使用 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;
}

但是,在运行时,代码会在 EXC_BAD_ACCESS 上捕获thing[2] = '4'。我认为这是因为代表字节的内存"123"被编译到我的程序的二进制文件中,在现代处理器/操作系统上被标记为code 而不是 data。(这个答案证实了 - 更不用说leaq 0x4d(%rip), %rsi ; "123"反汇编中有一行,将指针传递给相对于指令指针的地址!)

从自修改代码时代开始,C 允许这样做只是一个历史产物吗?我注意到我也可以void* x = main;毫无怨言地分配我正在丢弃修饰符。

这个答案说:

根据 C99 的基本原理,委员会中有些人希望字符串文字是可修改的,因此标准没有明确禁止它。

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

标签: ccompiler-warningsconst-correctness

解决方案


您引用的答案是没有引用的观点,坦率地说是胡说八道。它只不过是不破坏大量现有的遗留 C 代码,而这些代码希望在现代编译器中保持可编译性。

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

-Wwrite-strings

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

编译 C++ 时,警告从字符串文字到 char * 的不推荐转换。C++ 程序默认启用此警告。

CLANG 也有-Wwrite-strings, where 的同义词是-Wwriteable-strings

-Wwritable-strings

默认情况下启用此诊断。

还控制-Wdeprecated-writable-strings

诊断文本:

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

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

在 GCC 中-Wwrite-strings

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

产生:

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

铿锵生产:

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

推荐阅读