9

我读到了;

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

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

p指向包含3, 0, 3, 4和的五元素数组的第一个元素1

实际上我想知道,这个数组是否会存储在内存中,因为它没有名称?
换句话说,如果

char* str = "hello"

字符串"hello"将存储在内存中的什么位置?

4

4 回答 4

9

使用指针算法。所以

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

或者

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

事情就是这样。int在 C 中,对于基本类型(如and 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}是在记忆中;它将位于可执行文件的数据段中。您只是没有任何名称可以引用它。

于 2013-07-26T20:19:08.970 回答
5

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) 然后使用多字节字符序列来初始化一个静态存储持续时间和长度刚好足以包含该序列的数组。

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

于 2013-07-26T22:43:46.927 回答
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 中使用 While 类型的字符串字面量char[N]not 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指向第一个元素的指针和pis的类型int*&p类型是int**。(这类似于对 char 指针的字符串赋值)。

在第一种情况下p[i] = 10; 是合法的 0 <= i <= 4,但在第二种情况下,您正在编写只读内存 - 非法内存操作 - 分段错误。

观点:

不遵循也是有效的声明:

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

Q其实我想知道,这个数组会不会因为它没有名字而存储在内存中?

当然数组存储在内存中,但它只是由pp不是数组的名称)指向,没有其他名称。假设你这样做:

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"都成为数据部分中可执行文件的一部分。(您可以反汇编以找到那里的值)。

于 2013-07-26T20:28:08.443 回答
0

两种方式:

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

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

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

于 2013-07-26T20:23:31.877 回答