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

dsp*_*pjm 7 c linux compiler-construction gcc

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

编辑 - 添加评论示例

假设:

struct p
{
    char ch;
    int *arr;
};
Run Code Online (Sandbox Code Playgroud)

我们可以用这个:

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

p->arr = (struct p*)(p + 1);
Run Code Online (Sandbox Code Playgroud)

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

das*_*ght 13

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

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


*这种阵列也称为柔性阵列 ; 在C99中,你声明它们element_type flexArray[]而不是element_type flexArray[0],即你掉落零.


unw*_*ind 8

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

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

typedef struct {
  size_t length;
  int    data[0];
} IntArray1;
Run Code Online (Sandbox Code Playgroud)

和:

typedef struct {
  size_t length;
  int    *data;
} IntArray2;
Run Code Online (Sandbox Code Playgroud)

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

当然,第二种形式可以使阵列增长而不会冒"基本"地址(IntArray2结构本身的地址)发生变化的风险,这可能非常简洁.你不能这样做IntArray1,因为你需要将基本结构和整数数据元素分配在一起.权衡,权衡......


Kei*_*son 6

这些是所谓的"struct hack"的各种形式,在comp.lang.c FAQ的问题2.6中讨论过.

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

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

struct foo {
    size_t len;
    char str[1];
};
Run Code Online (Sandbox Code Playgroud)

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

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

struct foo {
    size_t len;
    char str[];
};
Run Code Online (Sandbox Code Playgroud)

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

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

struct bar {
    size_t len;
    char *ptr;
};
Run Code Online (Sandbox Code Playgroud)

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