C语言灵活阵列成员

例子

C99

类型声明

具有至少一个构件的结构可以在结构的末端另外包含单个未指定长度的阵列构件。这称为灵活数组成员:

struct ex1 
{
    size_t foo;
    int flex[];
};

struct ex2_header 
{
    int foo;
    char bar;
};

struct ex2 
{
    struct ex2_header hdr;
    int flex[];
};

/* Merged ex2_header and ex2 structures. */
struct ex3 
{
    int foo;
    char bar;
    int flex[];
};

对大小和填充的影响

在计算结构的大小时,可伸缩数组成员被视为没有大小,尽管该成员与该结构的前一个成员之间仍然存在填充:

/* Prints "8,8" on my machine, so there is no padding. */
printf("%zu,%zu\n", sizeof(size_t), sizeof(struct ex1));

/* Also prints "8,8" on my machine, so there is no padding in the ex2 structure itself. */
printf("%zu,%zu\n", sizeof(struct ex2_header), sizeof(struct ex2));

/* Prints "5,8" on my machine, so there are 3 bytes of padding. */
printf("%zu,%zu\n", sizeof(int) + sizeof(char), sizeof(struct ex3));

弹性数组成员被认为具有不完整的数组类型,因此无法使用来计算其大小sizeof。

用法

您可以使用包含灵活数组成员的结构类型来声明和初始化对象,但是由于将其视为不存在,因此您不能尝试初始化该灵活数组成员。禁止尝试这样做,否则将导致编译错误。

同样,以这种方式声明结构时,请勿尝试将值分配给柔性数组成员的任何元素,因为在结构末尾可能没有足够的填充以允许柔性数组成员所需的任何对象。但是,编译器不一定会阻止您执行此操作,因此这可能导致未定义的行为。

/* invalid: cannot initialize flexible array member */
struct ex1 e1 = {1, {2, 3}};
/* invalid: hdr={foo=1, bar=2} OK, but cannot initialize flexible array member */
struct ex2 e2 = {{1, 2}, {3}};
/* valid: initialize foo=1, bar=2 members */
struct ex3 e3 = {1, 2};

e1.flex[0] = 3; /* undefined behavior, in my case */
e3.flex[0] = 2; /* undefined behavior again */
e2.flex[0] = e3.flex[0]; /* undefined behavior */

您可以改为使用malloc,calloc或realloc为该结构分配额外的存储空间,然后再释放它,这使您可以根据需要使用灵活的数组成员:

/* valid: allocate an object of structure type `ex1` along with an array of 2 ints */
struct ex1 *pe1 = malloc(sizeof(*pe1) + 2 * sizeof(pe1->flex[0]));

/* valid: allocate an object of structure type ex2 along with an array of 4 ints */
struct ex2 *pe2 = malloc(sizeof(struct ex2) + sizeof(int[4]));

/* valid: allocate 5 structure type ex3 objects along with an array of 3 ints per object */
struct ex3 *pe3 = malloc(5 * (sizeof(*pe3) + sizeof(int[3])));

pe1->flex[0] = 3; /* valid */
pe3[0]->flex[0] = pe1->flex[0]; /* valid */
C99

``结构黑客''

柔性数组成员在C99之前不存在,因此被视为错误。常见的解决方法是声明一个长度为1的数组,此技术称为“结构破解”:

struct ex1 
{
    size_t foo;
    int flex[1];
};

但是,这将影响结构的大小,这与真正的柔性数组成员不同:

/* Prints "8,4,16" on my machine, signifying that there are 4 bytes of padding. */
printf("%d,%d,%d\n", (int)sizeof(size_t), (int)sizeof(int[1]), (int)sizeof(struct ex1));

要将flex成员用作灵活的数组成员,您需要malloc如上所述分配它,除了sizeof(*pe1)(或等效的sizeof(struct ex1))将被offsetof(struct ex1, flex)或更长的类型无关的表达式所代替sizeof(*pe1)-sizeof(pe1->flex)。或者,您可以从“柔性”数组的所需长度中减去1,因为假定所需长度大于0,它已经包含在结构大小中。相同的逻辑可以应用于其他用法示例。

兼容性

如果需要与不支持灵活数组成员的编译器兼容,则可以使用FLEXMEMB_SIZE如下定义的宏:

#if __STDC_VERSION__ < 199901L
#define FLEXMEMB_SIZE 1
#else
#define FLEXMEMB_SIZE /* nothing */
#endif

struct ex1 
{
    size_t foo;
    int flex[FLEXMEMB_SIZE];
};

分配对象时,应使用该offsetof(struct ex1, flex)格式来引用结构大小(不包括灵活数组成员),因为它是唯一的在支持灵活数组成员的编译器和不支持灵活数组成员的编译器之间保持一致的表达式:

struct ex1 *pe10 = malloc(offsetof(struct ex1, flex) + n * sizeof(pe10->flex[0]));

替代方法是使用预处理器有条件地从指定的长度中减去1。由于这种形式出现不一致和普遍人为错误的可能性增加,因此我将逻辑移到了一个单独的函数中:

struct ex1 *ex1_alloc(size_t n)
{
    struct ex1 tmp;
#if __STDC_VERSION__ < 199901L
    if (n != 0)
        n--;
#endif
    return malloc(sizeof(tmp) + n * sizeof(tmp.flex[0]));
}
...

/* allocate an ex1 object with "flex" array of length 3 */
struct ex1 *pe1 = ex1_alloc(3);