在union中模拟柔性数组成员的实现是否属于未定义行为?
问题:联合体结合侵入式存储的C标准合规性判断
我原本希望实现一个结构体,既能持有指向外部存储的指针,也能存储侵入式数据,理想结构如下:
struct S { int other_member; // not relevant to question // ... union { void *ptr; char buf[]; }; };
但标准C不允许联合体包含柔性数组成员(标准仅将柔性数组成员列为结构体的特例,未明确禁止但也不支持其在联合体中使用)。
于是我改用以下实现:
struct S { int other_member; // not relevant to question // ... union { void *ptr; char buf[ sizeof(void*) ]; }; };
当需要使用侵入式存储时,执行以下操作:
if ( buf_size < sizeof(void*) ) // just in case buf_size = sizeof(void*); struct S *s = malloc( sizeof(struct S) - sizeof(void*) + buf_size );
随后对s->buf读写类型为T的数据,其中sizeof(T) ≥ sizeof(void*)。
请问这种实现是否符合标准C要求?还是属于未定义行为?(我记得在柔性数组成员加入C标准前,声明buf[1]并malloc更大空间是常见做法)
备注:
- 仅使用标准C,不使用扩展特性;
struct S是节点,存在大量节点,若采用侵入式存储则所有节点数据尺寸相同,尺寸存储在其他位置。
解答
这种实现符合标准C的要求,不属于未定义行为,核心依据和分析如下:
结构体与内存分配的合法性
struct S是完全合法的标准C结构体:联合体中的buf是固定大小的数组(尺寸为sizeof(void*)),因此sizeof(struct S)是明确的固定值。你通过malloc分配的内存大小为sizeof(struct S) - sizeof(void*) + buf_size,本质是为结构体固定部分分配足够内存后,额外分配了buf_size - sizeof(void*)字节的空间——这和C99引入柔性数组成员前,用char buf[1]配合超大内存分配的经典兼容技巧逻辑完全一致,是标准允许的操作。内存访问的合规性
- 标准C允许访问
malloc分配的内存块中,超出结构体固定大小的部分,只要不越界整个分配的内存块。你的代码中确保buf_size ≥ sizeof(void*),因此分配的内存块至少能容纳完整的struct S,额外空间的访问是合法的。 - 联合体中
buf的地址与ptr的地址完全重合,因此buf具备void*的对齐属性。由于sizeof(T) ≥ sizeof(void*),且void*的对齐要求通常是系统中最严格的基本类型对齐之一,buf的地址可以满足T的对齐需求,不会出现对齐违规的未定义行为。
- 标准C允许访问
关键注意事项
- 必须严格保证
buf_size不小于sizeof(void*),你的代码中已经做了校验,这能避免分配的内存小于结构体固定大小,防止访问越界。 - 访问
s->buf的扩展部分时,必须严格遵循预先存储的buf_size尺寸,不能超出malloc分配的内存块边界,否则会触发未定义行为。
- 必须严格保证
内容的提问来源于stack exchange,提问作者Paul J. Lucas
相关产品推荐
相关产品推荐

