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

如何利用联合体规避严格别名违规?代码合法性相关问询

关于联合体规避严格别名规则的问题

背景

以下代码会触发未定义行为(违反严格别名规则):

uint16_t *buf = malloc(16); // 8*sizeof(uint16_t)
buf[1] = *buf = some_value;
((uint32_t *)buf)[1] = *(uint32_t *)buf;
((uint64_t *)buf)[1] = *(uint64_t *)buf;

我们可以向malloc()分配的内存写入任意类型,但不能通过指针转换读取以不兼容类型写入的值(char类型除外)。

问题

  1. 能否使用如下联合体规避严格别名违规导致的未定义行为?
union Data {
    uint16_t u16[8];
    uint32_t u32[4];
    uint64_t u64[2];
};

使用方式如下:

union Data *buf = malloc(16);
buf->u16[1] = buf->u16[0] = some_value;
buf->u32[1] = buf->u32[0];
buf->u64[1] = buf->u64[0];
  1. 由于uint16_t、uint32_t、uint64_t都是union Data的合法成员,能否将buf转换为上述任意类型的指针并解引用?即以下代码是否合法?
uint16_t first16bits = *(uint16_t *)buf;
uint32_t first32bits = *(uint32_t *)buf;
uint64_t first64bits = *(uint64_t *)buf;
  1. 如果上述使用union Data的代码仍不合法,那么联合体何时可以、何时不能用于编写不违反严格别名规则的合法代码(包括指针转换场景)?

解答

1. 直接通过联合体成员访问的代码完全合法

C标准明确规定:联合体的所有成员共享同一内存空间,通过联合体的任意成员访问其内存都是合法的,即使之前是通过其他成员写入的。你给出的第一种使用方式(直接通过buf->u16、buf->u32、buf->u64读写)完全符合规则,不会触发未定义行为——这正是C标准允许的、规避严格别名问题的标准做法。

2. 强制转换联合体指针为其他类型指针并解引用的行为不合法

你给出的第二种代码(*(uint16_t *)buf这类写法)仍然违反严格别名规则。原因在于:严格别名规则针对的是指针的类型,而非指针指向的内存实际是什么。这里你把union Data*强制转换成uint16_t*后,解引用时编译器会认为你是在访问一个uint16_t类型的对象,但实际指向的是union Data类型的对象——这两种类型不属于严格别名规则允许的兼容类型(除了char/unsigned char),因此这种转换解引用的行为依然是未定义行为。

简单来说:必须通过联合体的成员名来访问其内存,不能绕开联合体类型直接转成其他指针类型去访问。

3. 联合体合法使用的边界

要确保联合体的使用符合严格别名规则,需遵循以下原则:

  • 合法场景:
    • 直接通过联合体的成员变量读写内存,无论之前写入的是哪个成员。比如先写buf->u16[0],再读buf->u32[0],完全合法。
    • 将联合体指针转换为char*或unsigned char*进行内存操作(严格别名规则允许char类型访问任意对象)。
  • 非法场景:
    • 将联合体指针强制转换为非char类型的指针(比如uint32_t*)并直接解引用,无论该类型是否是联合体的成员。
    • 将非联合体类型的指针强制转换为联合体指针,再通过联合体成员访问(除非该指针原本指向的就是该类型的联合体对象)。

内容的提问来源于Stack Exchange,提问作者CPlus

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 14:45:33