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

时间:2018-06-19 22:59:10

标签: c pointers literals c-strings

据我了解,以下代码的工作方式如下:

char* cptr = "Hello World";

“ Hello World”位于程序存储器的.rodata部分中。字符串文字"Hello World"返回一个指向字符串基地址或所谓的“数组”中第一个元素的地址的指针,因为字符按顺序排列在内存中,所以它是'H '。这是我的小图,当我看到字符串文字存储在内存中时:

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

因此上面的声明变为:

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 : '\0'

再一次,地址是任意的,关键是堆栈中的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. 在代码中使用字符串文字是否会将基址返回到字符存储在内存中的“数组”?

3 个答案:

答案 0 :(得分:5)

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

此处声明中的=符号不是赋值运算符。而是,语法指定了要实例化的变量的初始化程序。在一般情况下,T x = y;T x; x = y;不同。

有一个语言规则,可以从字符串文字中初始化字符数组。 (在这种情况下,字符串文字不被“评估为其基址”)。 没有语言规则,可以从指向要复制到数组中的元素的指针初始化数组。

为什么这样的规则? “历史原因”。

答案 1 :(得分:3)

  

我假设与以前的情况一样,"Hello World"也会降级为'H'0x4的地址。

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

  

我假设"Hello World"的计算结果为其基地址0x4。在代码中使用字符串文字是否会将基地址返回到字符存储在内存中的“数组”?

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

  

为什么不能使用char指针作为临时占位符来存储字符串文字的基址?

可以。但是,一旦将字符串文字分配给char *,它就不再是字符串文字,至少就编译器而言。它成为char *指针,与其他char *指针没有什么不同。

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

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

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

答案 2 :(得分:3)

第二个定义char cString[] = "Hello World";是该等效定义的简写:

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

如果此定义出现在全局范围内或使用static存储,则cString将在.data段中,其初始内容在可执行映像中。如果发生在具有自动存储功能的范围之外,则编译器将为阵列分配自动存储(在堆栈框架上保留空间或等效空间),并生成代码以在运行时执行初始化。