灵活数组成员
灵活数组成员(Flexible Array Member,FAM)是 C99 引入的结构体特性,允许结构体最后一个成员是不完整数组类型。FAM 用于实现变长结构体,是动态大小数据结构的简洁实现方式。
基本语法
struct Packet {
uint32_t length;
uint8_t data[]; /* 灵活数组成员 */
};
注意:C99 标准语法是 data[],某些编译器也支持 data[0](GCC 扩展)。
分配和使用
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
struct Packet {
uint32_t length;
uint8_t data[];
};
/* 分配包含 100 字节数据的包 */
struct Packet *p = malloc(sizeof(struct Packet) + 100);
if (p != NULL) {
p->length = 100;
memcpy(p->data, "Hello", 5);
/* 使用 */
printf("Length: %u\n", p->length);
printf("Data: %.*s\n", p->length, p->data);
free(p);
}
与指针方式对比
/* 传统方式:两次分配 */
struct PacketOld {
uint32_t length;
uint8_t *data; /* 指针 */
};
struct PacketOld *p = malloc(sizeof(struct PacketOld));
p->data = malloc(100); /* 第二次分配 */
/* ... */
free(p->data);
free(p);
/* FAM 方式:一次分配 */
struct Packet *p = malloc(sizeof(struct Packet) + 100);
/* ... */
free(p); /* 一次释放 */
限制
struct Packet {
uint32_t length;
uint8_t data[];
};
/* 不能作为数组元素 */
/* struct Packet arr[10]; */ /* 错误 */
/* 不能作为其他结构体成员 */
/* struct Container { struct Packet p; }; */ /* 错误 */
/* sizeof 不包含 FAM */
printf("%zu\n", sizeof(struct Packet)); /* 4(只有 length) */
/* 必须动态分配 */
常见错误
忘记额外分配空间:
struct Packet *p = malloc(sizeof(struct Packet)); /* 错误:没有 data 空间 */
strcpy(p->data, "Hello"); /* 越界写入 */
/* 正确 */
struct Packet *p = malloc(sizeof(struct Packet) + strlen("Hello") + 1);
sizeof 误用:
struct Packet *p = malloc(sizeof(struct Packet) + 100);
printf("%zu\n", sizeof(*p)); /* 4,不是 104 */
最佳实践
- FAM 必须是结构体最后一个成员
- 动态分配时加上 FAM 所需大小
- 一次分配/释放,避免内存碎片
- 不将含 FAM 的结构体嵌入其他结构体
- 用
offsetof计算 FAM 偏移