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

C23标准(n3220)联合类型别名未定义行为条款问询

C23标准联合类型别名与未定义行为解析

一、联合类型别名的禁止规则

C标准的严格别名规则(6.5p7)定义了程序中允许通过不同类型左值访问同一对象的合法场景,其中与联合相关的核心规则是:

  • 若对象是联合类型,通过该联合的任意成员左值访问对象时,只要该成员类型与最近写入联合的成员类型兼容,或为字符类型,则属于合法访问;
  • 除此之外,通过与对象类型不兼容且不在规则允许列表中的左值访问对象,均属于违规,会触发未定义行为。

脚注93进一步补充:使用联合进行类型双关是标准允许的行为,但该许可仅适用于直接通过联合成员访问的场景——即左值必须是.或->运算符直接作用于联合对象的结果,而非脱离联合上下文的独立指针。


二、两段代码的未定义行为分析(基于C23草案n3220)

代码示例1

void f(int *a, char *b) {
  (*a)++;
  *a += *b;
}

int main() {
  union u {
    int a;
    char b;
  } x;
  x.a = 1;
  f(&x.a, &x.b);
}

问题分析

调用f前的代码均符合标准,但进入函数f后:

  • 参数a是指向int的指针,b是指向char的指针,两者指向同一个联合对象的存储区域,但在f的上下文里,这两个指针没有关联到原联合对象——编译器无法通过指针追溯到它们指向的是同一个联合的成员。
  • 根据6.5p7的严格别名规则,*b是char类型左值,访问的对象实际类型是int(因为最近写入的是x.a),且char与int不兼容,此时的访问不属于规则允许的场景。
  • 脚注93明确类型双关的许可仅针对直接访问联合成员的情况,这里的*b是脱离联合上下文的指针解引用,不属于合法场景。
  • 因此这段代码触发未定义行为,依据条款是6.5p7的严格别名规则。

代码示例2

union u {
  int a;
  char b;
};

void f(int *a, union u *x) {
  (*a)++;
  *a += x->b;
}

int main() {
  union u x;
  x.a = 1;
  f(&x.a, &x);
}

问题分析

在函数f的上下文里,x->b是直接通过联合指针访问成员的左值,符合脚注93允许的联合类型双关场景:

  • 虽然a指向x.a,x->b访问的是同一个联合对象,但x->b是联合成员的直接访问,编译器必须考虑到联合对象的存储可能被不同成员类型访问的情况,不能假设*a的修改不会影响x->b的取值。
  • 根据6.5p7和脚注93,这种通过联合成员直接访问的方式属于合法场景,因此这段代码严格合规,不会触发未定义行为。

补充疑问解答

  1. 关于示例1的合规性:通过无法追溯到原联合的不相容类型左值访问联合存储,属于违反严格别名规则的行为,脚注93的类型双关许可仅适用于直接访问联合成员的左值(即./->的直接结果)。
  2. 6.5p7与脚注93的看似矛盾:6.5p7是通用的别名规则,脚注93是对联合场景的特殊说明——联合成员的直接访问属于规则允许的例外,而脱离联合上下文的指针解引用则不适用该例外,二者不存在矛盾。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 00:41:04