复合/字符串文字在哪里存储在内存中?
我看了;
复合文字是C99function,可用于创建没有名称的数组。 考虑这个例子:
int *p = (int []){3, 0, 3, 4, 1};
p
指向包含3, 0, 3, 4
和1
个的五元素数组的第一个元素。
其实我想知道,这个数组是否会存储在内存中,因为它没有名字?
换言之,如果是
char* str = "hello"
字符串"hello"
将存储在内存中?
C 2011(N1570)6.5.2.5 5说:
如果复合文字出现在函数体外,则该对象具有静态存储持续时间; 否则,它具有与封闭块相关的自动存储持续时间。
因此,您显示的复合文字具有自动存储持续时间。 C标准没有指定此类对象在内存中的位置。 由C实现来组织内存。 通常,在堆栈上创建具有自动存储持续时间的对象,但是实现可以通过其他方法来管理这些对象。
特别是,假设您在其他位置记录p
的值,并递归调用包含此复合文字的例程。 当例程再次初始化p
时,复合文字的第二个实例。 它们实际上是不同的对象,C标准要求它们的地址不同。 因此,如果您打印两个指针,它们将具有不同的值。 但是,如果优化器能够确定您不这样做,并且永远不会比较指向复合文字的不同实例的两个指针,并且没有其他可以区分它们的可观察行为(由C标准定义) ,然后C实现可以自由使用复合文字的一个实际实例,而不是每次都创建一个新实例。 在这种情况下,编译器可以将复合文字保留在数据部分而不是堆栈中。
下面的代码演示了同一复合文字的两个实例具有不同的地址:
#include #include void foo(int *q) { int *p = (int []) { 2, 3 }; if (!q) foo(p); else printf("p = %p, q = %p.\n", (void *) p, (void *) q); } int main(void) { foo(0); return 0; }
字符串文字是不同的。 C 2011(N1570)6.4.5 6说:
在转换阶段7中,将值0的字节或代码附加到由字符串文字或文字产生的每个多字节字符序列。 78)然后使用多字节字符序列初始化静态存储持续时间和长度的数组,该数组足以包含序列。
所以字符串文字表示具有静态存储持续时间的对象。 即使包含它的例程被递归调用,它也只有一个实例。
使用指针算法。 所以
p[0], p[1], ...
要么
*p, *(p + 1), ...
这就是事情。 在C语言中,对于原始类型(如int
和char
,甚至是字符串文字),您都有很好的文字。 所以,我们可以很容易地说出类似的话
int length(char *s); int len = length("Hello, World!");
在C99中,添加了复合文字的概念来处理“数组文字”和“结构文字”。 因此,我们现在可以这样说:
int sum(int a[], int n); int total = sum((int []){ 17, 42 }, 2);
这是使用复合文字来表示“数组文字”。
其实我想知道,这个数组是否会存储在内存中,因为它没有名字?
是的,在记忆中。
我认为你的困惑源于此。 p
有一个名字。 (int []){3, 0, 3, 4, 1}
没有。 恰好p
的值是(int []){3, 0, 3, 4, 1}
。 当然(int []){3, 0, 3, 4, 1}
在内存中; 它将在您的可执行文件的数据段中。 你只是没有任何名称可以引用它。
从概念上讲,就像将字符串赋值给C中的char指针一样,类似地,您要将整数数组赋值给类型为int*
p
:
当你声明: int *p = (int []){3, 0, 3, 4, 1};
它可以假设存储在内存中,如:
p 23 27 31 35 36 37 +----+ +----+----+----+----+----+----+ | 23 | | 3 | 0 | 3 | 4 | 1 | ? | +----+ +----+----+----+----+----+----+ ▲ ▲ ▲ ▲ ▲ ▲ | | | | | // garbage value p p+1 p+2 p+3 p+4
所以基本上数组分配继续内存。 您可以按如下方式访问数组元素:
p[0] == 3 p[1] == 0 p[2] == 3 p[3] == 4 p[4] == 1
注意:
我们做char* str = "hello";
虽然C中的字符串文字类型是char[N]
而不是char*
。 但事实上,大多数表达式中char[N]
可以衰变为char*
。
点 :
声明数组时,例如:
int p[] = {3, 0, 3, 4, 1};
那么这里p
类型是int[5]
和&p
pointer to an array
=类型的pointer to an array
是: int(*)[5]
在声明中:
int *p = (int []){3, 0, 3, 4, 1};
指向第一个元素的p
指针&p
类型是int**
int*
, &p
type是int**
。 (这类似于字符串赋值给char指针)。
在第一种情况下, p[i] = 10;
0 <= i <= 4
是合法的,但在第二种情况下,你正在写只读内存 - 非法内存操作 - 分段错误。
点:
不遵循也是有效的声明:
int *p = (int *){3, 0, 3, 4, 1};
Q其实我想知道,这个数组是否会存储在内存中,因为它没有名字?
当然数组存储在内存中,但它只是由p
指向( p
不是数组的名称),没有其他名称。 假设您这样做:
int *p = (int []){3, 0, 3, 4, 1}; int i = 10; p = &i;
现在你丢失了数组的地址,它完全类似于:
char* s = "hello"; char c = 'A'; s = &c;
现在你失去了"hello"
的地址。
当你声明时,常量的内存来自静态段。 char字符串文字的任何int数组都存储在那里。 当您的声明运行分配给指针变量的地址时。 但是,常数没有任何名称 - 但价值。 在这两种情况下,数组和字符串"hello"
成为数据部分中可执行文件的一部分。 (你可以反汇编找到那些值)。
两种方式:
&((int[]){3,0,3,4,1})[3]
和
((int[]){3,0,3,4,1})+3
请注意,如果文字位于函数内部,则在退出封闭块时,指向它的指针将无效。