为什么我们使用零长度数组而不是指针?

时间:2013-06-03 10:08:34

标签: c linux gcc compiler-construction

据说零长度数组用于可变长度结构,我可以理解。但令我困惑的是为什么我们不仅仅使用指针,我们可以取消引用并以相同的方式分配不同的大小结构。

编辑 - 添加了评论示例

假设:

struct p
{
    char ch;
    int *arr;
};

我们可以使用这个:

struct p *p = malloc(sizeof(*p) + (sizeof(int) * n));

p->arr = (struct p*)(p + 1);

获得连续的内存块。但是,我似乎忘记了空间p->arr占用的空间,这似乎是零大小数组方法的不同之处。

4 个答案:

答案 0 :(得分:13)

如果使用指针,结构将不再具有可变长度:它将具有固定长度,但其数据将存储在不同的位置。

零长度数组 * 背后的想法是将数组的数据“与行”存储在结构中的其余数据中,以便数组的数据跟随结构的数据。记忆。指向单独分配的内存区域的指针不允许您这样做。

<小时/> * 此类数组也称为灵活数组;在C99中,您将其声明为element_type flexArray[]而不是element_type flexArray[0],即您将其归零。

答案 1 :(得分:8)

指针并不是真的需要,因此它没有任何好处而占用空间。此外,它可能意味着另一层次的间接,这也不是真正需要的。

比较这些示例声明,对于动态整数数组:

typedef struct {
  size_t length;
  int    data[0];
} IntArray1;

typedef struct {
  size_t length;
  int    *data;
} IntArray2;

基本上,指针表示“数组的第一个元素位于此地址,可以是任何”,它比通常需要的更通用。所需的模型是“数组的第一个元素就在这里,但我不知道数组有多大”。

当然,第二种形式可以增加数组,而不会冒“基础”地址(IntArray2结构本身的地址)发生变化的风险,这可能非常简洁。您无法使用IntArray1执行此操作,因为您需要将基本结构和整数数据元素一起分配。权衡,权衡......

答案 2 :(得分:6)

这些是所谓的“结构黑客”的各种形式,在comp.lang.c FAQ的问题2.6中讨论过。

在C中定义大小为0的数组实际上是非法的,并且至少自1989 ANSI标准以来。有些编译器允许它作为扩展,但依赖于它会导致不可移植的代码。

实现此目的的一种更便携的方法是使用长度为1的数组,例如:

struct foo {
    size_t len;
    char str[1];
};

您可以使用sizeof (struct foo)分配超过len个字节来跟踪分配的大小,然后访问str[N]以获取数组的第N个元素。由于C编译器通常不进行数组边界检查,因此这通常会“起作用”。但是,严格来说,这种行为是不确定的。

1999 ISO标准增加了一项名为“灵活阵列成员”的功能,旨在取代这种用法:

struct foo {
    size_t len;
    char str[];
};

您可以像旧的struct hack一样处理这些问题,但行为已明确定义。但你必须自己做所有的簿记;例如,sizeof (struct foo)仍然不包括数组的大小。

当然,您可以使用指针代替:

struct bar {
    size_t len;
    char *ptr;
};

这是一个非常好的方法,但它有不同的语义。 “struct hack”或灵活数组成员的主要优点是数组与结构的其余部分连续分配,您可以使用memcpy将数组与结构一起复制(只要目标已正确分配)。使用指针,数组将单独分配 - 这可能是您想要的,也可能不是。

答案 3 :(得分:1)

这是因为使用指针需要单独的分配和分配。

struct WithPointer
{
    int   someOtherField;
    ...
    int*  array;
};

struct WithArray
{
    int someOtherField;
    ...
    int array[1];
};

要获得WithPointer的“对象”,您需要执行以下操作:

struct WithPointer* withPointer = malloc(sizeof(struct WithPointer));
withPointer.array = malloc(ARRAY_SIZE * sizeof(int));

获取WithArray的'对象':

struct WithArray* withArray = malloc(sizeof(struct WithArray) + 
                                            (ARRAY_SIZE - 1) * sizeof(int));

就是这样。

在某些情况下,将数组放在连续的内存中也非常方便,甚至是必要的。例如,在网络协议包中。