7

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

编辑 - 从评论中添加示例

假设:

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

我们可以使用这个:

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

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

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

4

4 回答 4

14

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

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


*这样的数组也称为灵活数组;在 C99 中,您将它们声明为element_type flexArray[]而不是element_type flexArray[0],即您将其删除为零。

于 2013-06-03T10:11:16.833 回答
8

指针并不是真正需要的,因此它会浪费空间而没有任何好处。此外,它可能意味着另一个级别的间接,这也不是真正需要的。

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

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

和:

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

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

当然,第二种形式可以在不冒“基”地址(IntArray2结构本身的地址)更改的风险的情况下扩展数组,这非常简洁。你不能这样做IntArray1,因为你需要一起分配基本结构和整数数据元素。取舍,取舍……

于 2013-06-03T10:11:03.907 回答
6

这些是所谓的“struct hack”的各种形式,在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(只要目标正确分配)。使用指针,数组是单独分配的——这可能是也可能不是你想要的。

于 2013-06-03T21:00:53.713 回答
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));

而已。

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

于 2013-06-03T10:17:08.120 回答