第一个是 C99 的“灵活数组成员”。第二个是当您没有 C99 或更高版本时的可靠后备。
使用灵活的数组成员,您可以为数组分配所需的空间以及主结构:
A *pa = malloc(sizeof(A) + strlen(string) + 1);
pa->i = index;
strcpy(pa->buf, string);
...use pa...
free(pa);
就内存分配而言,buf
成员没有大小(所以sizeof(A) == sizeof(int)
除非由于数组对齐而存在填充问题——例如,如果你有一个灵活的数组double
)。
替代方案需要两个分配(和两个版本),或者在设置中需要注意:
typedef struct A2
{
int i;
char *buf;
} A2;
A2 *pa2 = malloc(sizeof(A2));
pa2->buff = strdup(string);
...use pa2...
free(pa2->buff);
free(pa2);
或者:
A2 *pa2 = malloc(sizeof(A2) + strlen(string) + 1);
pa2->buff = (char *)pa2 + sizeof(A2);
...use pa2...
free(pa2);
请注意,使用 A2 需要更多内存,无论是指针大小(单次分配),还是指针大小和第二次内存分配的开销(双重分配)。
你有时会看到一些被称为“struct hack”的东西正在使用中;这早于 C99 标准,并且已被灵活的数组成员淘汰。代码如下:
typedef struct A3
{
int i;
char buf[1];
} A3;
A3 *pa3 = malloc(sizeof(A3) + strlen(string) + 1);
strcpy(pa3->buf, string);
这和灵活的数组成员差不多,但结构更大。在示例中,在大多数机器上,该结构的A3
长度为 8 个字节(而不是 4 个字节A
)。
GCC 对零长度数组有一些支持;您可能会看到数组维度为 0 的 struct hack。这对于任何不模仿 GCC 的编译器都是不可移植的。
它被称为“struct hack”,因为语言标准不能保证它是可移植的(因为您访问的是声明数组的边界之外)。然而,从经验上看,它“一直有效”并且可能会继续这样做。尽管如此,您应该优先使用灵活的数组成员而不是 struct hack。
ISO/IEC 9899:2011 §6.7.2.1 结构和联合说明符
¶3结构或联合不应包含不完整或函数类型的成员(因此,结构不应包含自身的实例,但可能包含指向自身实例的指针),除了结构的最后一个成员具有多个命名成员可能具有不完整的数组类型;这样的结构(以及任何可能递归地包含此类结构的成员的联合)不应是结构的成员或数组的元素。
¶18 作为一种特殊情况,具有多个命名成员的结构的最后一个元素可能具有不完整的数组类型;这称为灵活数组成员。在大多数情况下,灵活数组成员被忽略。特别是,结构的大小就像省略了柔性数组成员一样,只是它可能具有比省略所暗示的更多的尾随填充。然而,当一个.
(或->
) 运算符的左操作数是(指向)具有灵活数组成员的结构,右操作数命名该成员,它的行为就好像该成员被替换为最长的数组(具有相同的元素类型),不会使结构大于被访问的对象;数组的偏移量应保持灵活数组成员的偏移量,即使这与替换数组的偏移量不同。如果这个数组没有元素,它的行为就好像它有一个元素,但如果尝试访问该元素或生成一个越过它的指针,则行为是不确定的。