复合/字符串文字在哪里存储在内存中?

时间:2013-07-26 20:17:03

标签: c pointers compound-literals

我读到了;

  

复合文字是C99功能,可用于创建没有名称的数组。考虑一下这个例子:

int *p = (int []){3, 0, 3, 4, 1};
  

p指向包含3, 0, 3, 41的五元素数组的第一个元素。

其实我想知道,这个数组是否会存储在内存中,因为它没有名称? 换言之,在

的情况下
char* str = "hello"

字符串"hello"将存储在内存中?

4 个答案:

答案 0 :(得分:9)

使用指针算法。所以

p[0], p[1], ...

*p, *(p + 1), ...

这就是事情。在C语言中,对于intchar等基本类型,甚至是字符串文字都有很好的文字。所以,我们可以很容易地说出像

这样的话
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}在记忆中;它将位于您的可执行文件的数据段中。你没有任何名称可以引用它。

答案 1 :(得分:4)

从概念上讲,就像将字符串赋值给C中的char指针一样,类似地,您将整数数组分配给p类型的int*
当您声明: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 =类型为:int(*)[5]

而在声明中:

int *p = (int []){3, 0, 3, 4, 1};

p指向第一个元素的指针和p的类型为int*&p类型为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"都成为数据部分中可执行文件的一部分。 (您可以反汇编以找到值)。

答案 2 :(得分:4)

C 2011(N1570)6.5.2.5 5说:

  

如果复合文字出现在函数体外,则该对象具有静态存储持续时间;否则,它具有与封闭块相关的自动存储持续时间。

因此,您显示的复合文字具有自动存储持续时间。 C标准没有指定此类对象在内存中的位置。由C实现来组织内存。通常,在堆栈上创建具有自动存储持续时间的对象,但实现可以通过其他方法来管理此类对象。

特别是,假设您在其他位置记录p的值,并递归调用包含此复合文字的例程。例程再次初始化p时,复合文字的第二个实例。它们实际上是不同的对象,C标准要求它们的地址不同。因此,如果您打印两个指针,它们将具有不同的值。但是,如果优化器能够确定您不执行此操作,并且永远不会比较指向复合文字的不同实例的两个指针,并且没有其他可以区分它们的可观察行为(由C标准定义) ,然后C实现可以自由地使用复合文字的一个实际实例,而不是每次都创建一个新实例。在这种情况下,编译器可以将复合文字保留在数据部分而不是堆栈中。

以下代码演示了同一复合文字的两个实例具有不同的地址:

#include <math.h>
#include <stdio.h>


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中,将值为零的字节或代码附加到由字符串文字或文字产生的每个多字节字符序列。 78)然后使用多字节字符序列初始化静态存储持续时间和长度的数组,足以包含序列。

因此字符串文字表示具有静态存储持续时间的对象。即使包含它的例程被递归调用,它也只有一个实例。

答案 3 :(得分:0)

两种方式:

&((int[]){3,0,3,4,1})[3]

((int[]){3,0,3,4,1})+3

请注意,如果文字位于函数内部,则退出封闭块时指针将无效。