当从字符串文字初始化 char 数组时会发生什么?

What happens when a char array gets initialized from a string literal?

据我了解,下面的代码是这样工作的:

char* cptr = "Hello World";

"Hello World" 位于程序内存的 .rodata 部分。字符串文字 "Hello World" return 是指向字符串基地址的指针,或者所谓的 "array" 中第一个元素的地址,因为字符在内存中按顺序排列它将是 'H'。这是我的小图,因为我将字符串文字可视化存储在内存中:

0x4 : 'H'
0x5 : 'e'
0x6 : 'l'
0x6 : 'l'
0x7 : 'o'
0x8 : ' '
0x9 : 'W'
0xa : 'o'
0xb : 'r'
0xc : 'l'
0xd : 'd'
0xe : '[=11=]'

所以上面的声明变成:

char* cptr = 0x4;

现在 cptr 指向字符串文字。我只是在编地址。

0xa1 : 0x4

现在这段代码是如何工作的?

char cString[] = "Hello World";

我假设在之前的情况下 "Hello World" 也降级为 'H' 和 0x4 的地址。

char cString[] = 0x4;

= 与 char 数组的初始化一起使用时,我将其视为重载赋值运算符。据我了解,仅在初始化 C 字符串时,它会从给定的基地址开始逐个字符地复制到 C 字符串中,直到它在复制的最后一个字符中遇到 '\0'。它还为所有字符分配足够的内存。因为重载运算符实际上只是函数,所以我假设它的内部实现类似于 strcpy().

我希望一位更有经验的 C 程序员确认我对这段代码如何工作的假设。这是我将字符串文字中的字符复制到其中后的 C 字符串的可视化:

0xb4 : 'H'
0xb5 : 'e'
0xb6 : 'l'
0xb6 : 'l'
0xb7 : 'o'
0xb8 : ' '
0xb9 : 'W'
0xba : 'o'
0xbb : 'r'
0xbc : 'l'
0xbd : 'd'
0xbe : '[=16=]'

再一次,地址是任意的,重点是堆栈中的 C 字符串与内存中 .rodata 部分中的字符串文字不同。

我想做什么?我正在尝试使用 char 指针临时保存字符串文字的基地址,并使用相同的 char 指针(字符串文字的基地址)来初始化 C 字符串。

char* cptr = "Hello World";
char cString[] = cptr;

我假设 "Hello World" 的计算结果为其基地址 0x4。所以这段代码应该是这样的:

char* cptr = 0x4;
char cString[] = 0x4;

我假设它应该与 char cString[] = "Hello World"; 没有什么不同,因为 "Hello World" 求出它的基地址,这就是存储在 char 指针中的内容!

但是,gcc 给我一个错误:

error: invalid initializer
char cString[] = cptr;
                 ^
  1. 为什么不能使用 char 指针作为临时占位符来存储字符串文字的基地址?
  2. 这段代码是如何工作的?我的假设是否正确?
  3. 是否在代码中使用字符串字面值 return "array" 字符存储在内存中的基地址?

您对内存布局的理解或多或少是正确的。但是您遇到的问题是 C 中的初始化语义之一。

此处声明中的 = 符号不是赋值运算符。相反,它是为被实例化的变量指定初始值设定项的语法。在一般情况下,T x = y;T x; x = y; 不同。

有一个语言规则,字符数组可以从字符串文字初始化。 (在此上下文中,字符串文字不是 "evaluated to its base address")。 不是 一种语言规则,即数组可以从指向要复制到数组中的元素的指针初始化。

为什么会有这样的规定? "Historical reasons".

I am assuming that as in the previous situation "Hello World" also degrades to the address of 'H' and 0x4.

不是真的:cString[] 在内存中获得一个全新的地址。编译器为其分配了 12 chars,并用 "Hello World" 字符串文字的内容初始化它们。

I assume that "Hello World" evaluates to its base address, 0x4. Does using a string literal in the code return the base address to the "array" where the chars are stored in the memory?

cString 稍后可能会 转换 char*,产生其基地址,但它在常规上下文中仍然是一个数组。特别是,如果您调用 sizeof(cString),您将获得数组的大小,而不是指针的大小。

How come you can't use a char pointer as a temporary placeholder to store the base address of a string literal?

可以。但是,一旦将字符串文字分配给 char *,它就不再是字符串文字,至少就编译器而言是这样。它变成了一个 char * 指针,与其他 char * 指针没有区别。

请注意,现代 C 编译器将相同的字符串文字组合为优化,因此如果您编写

#define HELLO_WORLD "Hello World"
...
char* cptr = HELLO_WORLD;
char cString[] = HELLO_WORLD;

并打开优化,编译器将消除字符串文字的重复副本。

第二个定义 char cString[] = "Hello World"; 是此等效定义的 shorthand:

char cString[12] = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '[=10=]' };

如果此定义出现在全局范围内或使用 static 存储,cString 将位于 .data 段中,具有可执行映像中的初始内容。如果它出现在具有自动存储的函数范围之外,编译器将为数组分配自动存储(在堆栈帧或等效项上保留 space)并生成代码以在 运行- 处执行初始化时间.