数组初始化的区别

3
请看下面的陈述:
char a[5]="jgkl"; // let's call this Statement A
char *b="jhdfjnfnsfnnkjdf"; // let's call this Statement B , and yes i know this is not an Array
char c[5]={'j','g','k','l','\0'}; // let's call this Statement C

现在,语句A和C之间有什么区别吗?我的意思是它们都应该在堆栈上,不是吗?只有b会在静态位置。那么,“jgkl”不会在整个程序的生命周期中存在于静态位置吗?因为它应该是只读/常量?请澄清。
5个回答

5
不,因为语句A中的字符“jgkl”被用来初始化a,它不会在可执行文件中创建一个字符字符串的存储空间(除了您声明a时创建的存储空间)。此声明在读写内存中创建了一个字符数组,其中包含字节{'j','g','k','l','\ 0'},但用于初始化它的字符串在可执行结果中不存在。
在语句B中,字符串文字的地址用作初始化程序。变量char * b是存储在读写内存中的指针。它指向字符串“jhdfjnfnsfnnkjdf”。这个字符串存在于可执行映像中的一个段中,通常称为“.sdata”,意思是“静态数据”。该字符串通常存储在只读内存中,符合C标准的规定。
这是声明字符数组和字符串常量之间的一个关键区别:即使您有指向字符串常量的指针,也不应修改其内容。
尝试修改字符串常量在ANSI C标准第6.5.7节中被定义为“未定义行为”。

@Heath:在 C 语言中,字符串字面量实际上不是 const 限定的(主要是出于历史原因)。尽管如此,程序仍然不允许对它们进行写操作。 - Gilles 'SO- stop being evil'
@Zan - 我刚做了几个实验,发现你是正确的。我已经有20年没在任何编译器中“测试”过这个了。 ;) - Heath Hunnicutt
是的,那是一个字符串字面量。特别地,传递给 printf() 的是该字符串字面量的地址。 - Heath Hunnicutt
1
语句A中的"jgkl"确实是一个字符串字面值。它是一个字符串字面值,因为它是双引号之间的字符序列。字符串字面值用作数组a的初始化器。当字符串字面值用作初始化器时,其内容被复制到数组中。关于答案的最后一句话,行为不是实现定义的。如果尝试修改字符串字面值,则行为是未定义的(参见§6.4.5/6和§6.7.8/32)。 - James McNellis
@strut,James说得对,这是标准词汇中的字符串字面值,那是我的错误。我正确的意思是用于初始化数组的字符串字面值不会成为可执行文件的一部分,这就是我所说的“不是字符串字面值”的意思。它对于解析器来说是一个字符串字面值,但对于代码生成器来说不是。至于Bjarne Stroustrup关于const的评论,那是C++,所以谁知道呢?你问的是C。关于James的引用,他似乎在使用C99标准。我的引用是ANSI标准。 - Heath Hunnicutt
显示剩余5条评论

2
如果a[]是静态的,那么c[]也是静态的 - 这两者是等价的,而且都不是字符串字面量。这两个数组也可以被声明为在堆栈上,具体取决于它们在哪里和如何声明,而不是用于指定它们内容的语法。

@strut 不是这样的,它们都是数组,可以在初始化后更改其内容,而 b 的内容无法修改。你误解了你所读的内容。 - anon
a[] = "foo" 中,a 是一个数组,"foo" 是一个字面量。 - anon

2

“jgkl”这个值可能永远不会被加载到工作内存中。在调用main函数之前,会运行一个函数(通常称为cinit),其中一项任务是初始化静态和文件范围的变量。在我使用的DSP编译器中,初始值存储在程序映像的表格中。该表格的格式与正在初始化的变量的格式无关。初始化程序表格仍然是程序映像的一部分,从未复制到RAM中。简单地说,我无法可靠地在内存中访问“jgkl”。

a这样的小字符串可能根本没有存储在该表格中。优化器可以将其缩减为等效的(伪指令)store reg const(152<<24|167<<16|153<<8|154)

我怀疑大多数编译器都是类似的。


1

A和C是完全等价的。A中使用的语法是C中语法的缩写。

名为a和c的对象都是长度为5的字节数组,存储在内存中的某个固定位置。程序可以随时更改元素字节。初始化对象的方式由编译器决定。编译器可能生成类似于a[0] = 'j'; a[1] = 'g'; ...或类似于memcpy(a, static_read_only_initialization_data[1729], 5)的代码,也可能是其他任何选择。如果声明发生在函数中,则该数组在(概念上的)堆栈上;如果声明发生在文件范围内,则在全局可写内存中。

名为b的对象是一个指向字节的指针。它的初始值是指向字符串字面值内存的指针,在许多具有只读内存的实现中该内存是只读的,但并非所有情况都如此。b的值可能会更改(例如指向不同的字符串或NULL)。程序不被允许更改“jhdfjnfnsfnnkjdf”的内容,尽管通常情况下,C的实现可能不强制执行这一点。


0

C-Literals总是只读的。

  • a) 分配5个字节的内存,并将文字内容(包括'\0')存储在其中
  • b) 分配sizeof(size_t)字节的内存,并将文字地址存储在其中
  • c) 分配5个字节的内存,并将5个字符值存储在其中

在(a)中,没有字面量。在这种情况下,您永远无法获取任何地址以查找char *到“jgkl”作为字符串字面量。 - Heath Hunnicutt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接