关于C18标准6.5节有效类型限制的澄清与疑问
C18标准有效类型规则与联合访问的未定义行为分析
先明确C18标准的核心规则
C18 §6.5.6(有效类型定义):
对象的有效类型是其声明类型(若对象有声明)。若通过无声明类型的左值访问对象存储值,则该左值的类型成为对象的有效类型。若通过字符类型左值访问,不改变有效类型。对于动态分配的对象,首次写操作的左值类型即为有效类型;若未被写入,可使用任何兼容类型。对于联合对象,当访问其某个成员时,该联合对象的有效类型即为当前被访问成员的类型。
C18 §6.5.7(对象存储访问规则):
对象的存储值只能通过以下类型之一的左值表达式访问:
- 与对象有效类型兼容的类型;
- 上述类型的限定版本;
- 与有效类型对应的有符号/无符号类型;
- 上述类型的限定版本;
- 包含上述类型作为成员的聚合/联合类型(递归包含子聚合/子联合成员);
- 字符类型。
第一个案例的未定义行为分析
给出的代码及调用上下文:
short g(int *p, short *q) { short z = *q; *p = 10; return z; } union int_or_short { int x; short y; } u = { .y = 3 }; int *p = &u.x; short *q = &u.y; return g(p, q);
内联函数体后逻辑更清晰:
union int_or_short { int x; short y; } u = { .y = 3 }; int *p = &u.x; short *q = &u.y; short z = *q; // 通过short类型左值访问联合对象u *p = 10; // 通过int类型左值写u的存储
违规原因:
- 执行
short z = *q;时,通过short类型左值访问联合对象u,根据§6.5.6规则,u的有效类型被设置为short。 *p = 10;是写操作,属于标准定义的“访问对象存储值”范畴(读、写操作都算访问)。此时p解引用后是int类型左值,而u当前有效类型是short——int与short不属于兼容类型,完全不符合§6.5.7的访问规则,因此触发未定义行为。
第二个案例为何合法?
对比代码:
union int_or_short { int x; short y; } u = { .y = 3 }; short z = u.y; u.x = 10;
合法原因:
C标准明确允许联合的核心用途——通过任意成员复用同一块内存。当执行u.x = 10;时,这是直接通过联合对象访问其成员:
- 根据§6.5.6规则,访问联合的
x成员会自动将u的有效类型更新为int; - 这种访问方式属于§6.5.7中允许的“联合类型包含对应成员”的场景,完全符合有效类型规则,因此是合法的,不存在未定义行为。
核心疑问:*p=10与u.x=10的本质区别
*p=10:p是指向联合成员x的独立指针,此时联合对象的有效类型是short(此前通过y成员访问过)。这里是绕过联合对象的直接访问,用int类型左值访问有效类型为short的对象,违反访问规则。u.x=10:直接通过联合对象访问成员,标准会自动更新联合的有效类型为当前成员的类型,且属于联合设计允许的内存复用场景,完全符合规则。
内容的提问来源于stack exchange,提问作者Steve Siegel
相关产品推荐
相关产品推荐

