最近我看到了一个结构体定义,
struct arr {
int cnt;
struct {
int size;
int *name;
} list[0];
};
现在我不知道为什么要声明list[0]
。 我感兴趣的是为什么要使用它。 它有任何优点吗? 如果有,是什么?
这是为了使用动态长度数组。您可以使用malloc()
分配内存,并使数组驻留在结构的末尾:
struct arr *my_arr = malloc(sizeof *my_arr + 17 * sizeof *my_arr->list);
my_arr->cnt = 17;
my_arr->list[0].size = 0;
my_arr->list[1].name = "foo";
实际上,能够使用0作为长度是GCC的一个扩展功能(正如评论中所指出的)。在C99中,您可以完全省略大小文字面量以达到相同的效果。
在这些功能被实现之前,通常将长度设置为1,但这会使分配过程变得更加复杂,因为您必须在计算需要的内存时进行补偿。
list
不会对结构体的大小产生贡献。 sizeof(struct arr) == 4
- Jeff Mercado0
的能力是GCC的特性。C99的等效方法是完全省略长度,例如struct { ... } list[];
。 - Bart van Ingen Schenaumalloc
完成。如果使用指针,则必须单独分配内存(两个malloc
调用,可能不连续),或者使用其他技巧(以实现适当的对齐等)。 - AnT stands with Russia这被称为“结构体技巧”。您可以在SO或网络上搜索它。
请注意,在C中正式地声明大小为0的数组始终是非法的。您提供的代码正式上甚至无法编译。大多数C编译器将接受0大小的数组声明作为扩展,特别是因为它经常用于“懒惰”的“结构体技巧”(它可以依靠sizeof
来确定要分配多少内存,因为0大小的数组据说不会影响结构体的总大小)。
一个可以说更好的结构体技巧实现使用大小为1的数组。
struct arr {
int cnt;
struct {
int size;
int *name;
} list[1];
};
它更好是因为它至少可以正式编译。为了为具有 N
个元素的结构体在 list
中分配内存,使用标准的 offsetof
宏。
arr *a = malloc(offsetof(arr, list) + N * sizeof a->list);
[]
)得到支持,因为在C99中,0大小的数组声明也是不合法的。unsigned char foo[1];
的所有访问编译为 foo[0]
的内容?虽然回答“struct-hack”问题的人们认为大小为1的数组版本是否是“未定义行为”是学术性的问题,但前述优化在某些嵌入式系统环境中可能非常有用,如果合法的话。是否可以这样做(添加警告,编译器可能希望显式禁用对间接访问结构体的最后一个元素)? - supercatb
小于a
中元素数量时才成立。如果结构体内的数组指针只能在声明大小或分配空间中较小的一个范围内进行索引,那么你描述的情况将是未定义行为。如果我负责标准,我会规定访问超出指定范围的数组将是未定义行为,除非在结构体末尾的单个元素数组可以使用非零索引,如果该结构体是... - supercatmalloc
、calloc
等函数分配的内存段中的最后一个项目。这样的规则将要求编译器继续支持使用“结构体hack”的旧代码,但允许大多数结构体数组强制执行所允许的优化(不仅仅是我提到的单个元素情况,还包括许多其他涉及别名等情况的场景)。例如,对于struct {int a[1], b;} foo; foo.b=3; x=foo.a[something()]; foo.b=0;
,编译器是否需要在访问foo.a[]
之前将3
存储到内存中,或者是否允许省略存储? - supercatcnt
为0,则数据大小可能仅为cnt
的长度。
我在此确认我所担心的,即list[0]
无效。
[1]
来浪费一点空间(或者计算来弥补它),或者使用[]
(但这需要一个 C99 编译器)。 - R.. GitHub STOP HELPING ICE