为什么字符串文字根据声明为“char *”或“char[]”存储在不同的位置?

Sam*_*Sam 1 c assembly c-strings

我对这个简单的事情进行了尴尬的斗争,因为这是段错误:

#include <stdio.h>

int main()
{
    char *test = "this is a string";
    test[0] = 'q';
    printf(test);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

但这不是:

#include <stdio.h>

int main()
{
    char test[] = "this is a string";
    test[0] = 'q';
    printf(test);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

查看程序集后,我注意到在第一种情况下,文字"this is a string"是在 中声明的.rodata,因此这解释了段错误。但在第二种情况下,字符串根本不在程序集中,因此我假设它是通过 .data 部分链接为可写的。为什么会有这种行为差异?这很明显吗?我很愚蠢吗?

Ser*_*sta 5

这很明显吗?应该是因为它是C语言的一个重要特性:即使由于历史原因没有声明为const,字符串文字也是不可变的。让我们仔细看看:

char *test = "this is a string";
Run Code Online (Sandbox Code Playgroud)

这(错误地)声明了一个指向 const 字符数组的非常量指针。从那时起,通过该指针更改字符会显式调用未定义的行为。这里你遇到了一个段错误,但是编译器可以自由地忽略该尝试,退出程序而不显示任何消息,甚至<在此处输入你最糟糕的噩梦> ...

一个像样的编译器应该警告你这一点(警告不能被忽略......)并且正确的语法是const char *test = "this is a string";.


char test[] = "this is a string";
Run Code Online (Sandbox Code Playgroud)

这(正确地)声明了一个字符数组,并通过字符串文字的副本初始化其内容。如果您提供初始值设定项文字字符串并使用初始值设定项的大小作为数组的大小,该语言足够允许您给出空大小。从那时起,更改数组的字符就是合法的操作。

你应该记住什么:

  1. 字符串文字是 const
  2. 数组和指针是不同的动物。数组保存一些数据,而指针仅指向其他地方存在的一些数据。当您将数组用作值时,数组将衰减为指针(这里正确的措辞是右值...)。

  • 字符串文字在 C 中不是“const”。编译器不需要发出警告(事实上,如果编译器拒绝编译程序,则它是不合格的)。当添加字符串文字时,“const”甚至不存在于语言中,并且它们的类型从未改变。参见 C11 6.4.5/6“数组元素的类型为‘char’” (4认同)