据说零长度数组用于可变长度结构,我可以理解。但令我困惑的是为什么我们不仅仅使用指针,我们可以取消引用并以相同的方式分配不同的大小结构。
编辑 - 添加了评论示例
假设:
struct p
{
char ch;
int *arr;
};
我们可以使用这个:
struct p *p = malloc(sizeof(*p) + (sizeof(int) * n));
p->arr = (struct p*)(p + 1);
获得连续的内存块。但是,我似乎忘记了空间p->arr
占用的空间,这似乎是零大小数组方法的不同之处。
答案 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));
就是这样。
在某些情况下,将数组放在连续的内存中也非常方便,甚至是必要的。例如,在网络协议包中。