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

为何开启/关闭-fsanitize=undefined,address时C代码运行结果不一致

问题解答

问题1:代码是否存在问题?取共用体多个成员的地址属于非法操作吗?

取共用体多个成员的地址本身是完全合法的,C语言标准明确支持该操作,你的代码的问题出在以下两处:

  • 共用体跨成员读取属于实现定义行为:你在循环中最后一次写入的是共用体的c(int类型)成员,最后读取的是b(short类型)成员,这种读取非最后写入的共用体成员的行为,结果完全取决于平台的字节序、类型长度、内存布局,没有统一的标准规定返回值。
  • 违反严格别名规则导致优化结果差异:你的代码通过long *h和int *e两个不同类型的指针访问同一块共用体内存,这不符合C语言的严格别名规则——该规则默认不同类型的指针不会指向同一块内存,编译器可以基于这个规则做指令重排、无效操作消除等优化。开启-fsanitize=undefined,address时,编译器会关闭严格别名相关优化并插入大量运行时检查,所以*e = 6的写入会正常生效,最终读取d.b得到6;而在-O1且无sanitizer的情况下,编译器认为*h和*e指向的内存无重叠,直接优化掉了*e = 6的写入操作,所以最终输出为0。

问题2:如何让LLVM或GCC给出相关告警?

-Wall和-Wextra默认没有开启严格别名相关的告警,你可以通过添加以下编译选项实现告警或解决优化差异:

  • 开启严格别名告警:添加-Wstrict-aliasing选项,GCC和Clang都会对可能违反严格别名规则的代码给出告警。你也可以调整告警等级:-Wstrict-aliasing=1为最高检查等级,-Wstrict-aliasing=2是默认等级,可覆盖绝大多数违规场景,-Wstrict-aliasing=3为最低检查等级。
  • 如果你确认代码中需要使用这类类型双关操作,可以添加-fno-strict-aliasing编译选项关闭严格别名优化,此时无论是否开启sanitizer,程序运行结果都会保持一致。
  • 针对共用体跨成员读取的实现定义行为,目前GCC和Clang都没有对应的通用告警选项,因为这类用法在低级别系统编程中是常见的合法用法,只是结果依赖平台实现。

内容的提问来源于stack exchange,提问作者Suo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 14:57:05