You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言中使用联合的指针成员进行类型双关是否属于未定义行为(UB)?

C语言中使用联合的指针成员进行类型双关是否属于未定义行为(UB)?

这个问题的答案得结合C标准规则和具体平台的实现细节来看,不能一概而论——在大多数主流平台(比如x86_64、ARM)上,你的代码是安全且符合预期的,但从C标准的严格定义来说,它存在潜在的未定义行为风险,具体原因如下:

先看C标准的核心规则

以C17标准为例,它对联合的类型双关有明确规定:

当读取联合的非活跃成员(即最后一次赋值的成员之外的成员)时,会将该成员对应的内存表示重新解释为新成员类型的对象表示(这个过程常被称为“类型双关”),除非这个新的对象表示是陷阱表示,否则行为是定义明确的。

放到你的场景里,需要判断两个关键问题:

  1. int_sequence和untyped_sequence的内存布局是否兼容?
  2. 将int*的对象表示重新解释为void*是否会产生陷阱表示?

1. 结构体的内存布局兼容性

你的两个结构体定义:

typedef struct {
    void* data;
    size_t size;
    size_t item_size;
} untyped_sequence;

typedef struct {
    int* data;
    size_t size;
    size_t item_size;
} int_sequence;

它们的成员顺序、数量完全一致,唯一区别是第一个成员的指针类型不同。在几乎所有主流平台上:

  • void*和int*的大小相同(都是平台的指针宽度,比如64位系统上是8字节)
  • 结构体的填充规则一致(两个结构体都不会在成员之间产生额外的填充字节)

所以它们的内存布局完全重合,联合中typed和untyped成员的每个字节都对应相同位置。但C标准不强制要求不同指针类型的大小相同,也不强制要求不同结构体(即使成员布局相似)的填充规则一致——如果在某个特殊平台上,void*的大小比int*大,或者结构体有不同的填充,那两个结构体的内存布局就不兼容,此时访问非活跃成员就是UB。

2. 指针类型的对象表示与陷阱表示

C标准允许不同的指针类型有不同的对象表示,也允许指针类型存在陷阱表示(即某些内存值不是有效的指针值)。比如在一些古老的分段内存平台上,int*和void*可能有不同的段属性,此时把int*的内存表示直接当作void*使用,就会得到一个无效的void*,这属于陷阱表示,访问它就是UB。

但在主流的平坦内存模型平台上,void*和int*的对象表示完全相同,不存在陷阱表示的问题,所以这种类型双关是安全的。

你的代码为什么在主流编译器上能正常工作?

GCC、Clang、MSVC这些主流编译器都针对常见平台做了优化,它们默认假设指针类型的大小和表示一致,结构体的填充规则也统一,所以会正确处理这种联合类型双关的场景,不会产生警告,运行结果也符合预期。但这只是编译器的实现行为,不是C标准的强制要求。

如何避免潜在的UB?

如果你想写出完全符合C标准、没有UB风险的代码,不需要用联合来做类型双关,直接显式构造untyped_sequence对象即可:

// 替换原来的调用
untyped_zero_first((untyped_sequence){
    .data = s.typed.data,
    .size = s.typed.size,
    .item_size = s.typed.item_size
});

这种方式通过显式转换和初始化,完全符合C标准的规则,在任何平台上都是安全的。

备注:内容来源于stack exchange,提问作者Johann Gerell

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 10:18:02