首页 > 解决方案 > 使用 union 在 C 中键入双关语

问题描述

考虑以下代码:

union u{
    int i;
    long j[2];
};

int main(void){
    union u *u = malloc(sizeof *u);
    u->i = 10;
    printf("%li\n", u->j[0]);
}

我想解释代码的合法性6.5

对象的存储值只能由具有以下类型之一的左值表达式访问:

— 与对象的有效类型兼容的类型,

[...]

— 在其成员中包含上述类型之一的聚合或联合类型(递归地,包括子聚合或包含联合的成员),或

将此应用于上面的示例,我们有:

  1. u->i = 10;使u->i对象具有有效的类型int
  2. 左值uunion类型包含 type 的成员int
  3. 使用具有 type 成员的类型u->j[0]的左值访问具有未指定值的对象。uunion uint
  4. 应用我们的报价6.5,这里没有 UB。

问:这样的推理正确吗?或者它包含一些错误?

标签: clanguage-lawyeruniontype-punning

解决方案


是的,你的推理是正确的。这不是未定义的行为,而是根据 C11 第 6.2.6.1/7 节的未指定行为:

当值存储在联合类型对象的成员中时,对象表示中不对应于该成员但对应于其他成员的字节采用未指定的值。

第 3.19.3 节阐明了这意味着什么:

未指定值:相关类型的有效值,本国际标准对在任何情况下选择哪个值没有要求

附件 J中提醒了这一点:可移植性问题

J.1 未指定行为
1 以下未指定:
— ...
— 在结构或联合中存储值时填充字节的值 (6.2.6.1)。
— 对应于联合成员的字节值,而不是最后存储到 (6.2.6.1) 中的成员。
— ...

J2 中没有指定关于访问联合成员的任何内容,这与未定义的行为有关

话虽如此,可移植性问题可能很严重,因为第 6.2.6.1/6 节提醒:

结构或联合对象的值绝不是陷阱表示,即使结构或联合对象的成员的值可能是陷阱表示。

陷阱表示是“不需要表示对象类型的值的对象表示”(定义),可以理解为“获取陷阱表示可能会执行陷阱但不是必需的”(脚注)。所以访问不活动的值可能会导致程序中断,但如果没有,只是没有保证。


推荐阅读