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

