使用union在C中实现类型双关
考虑以下代码:使用union在C中实现类型双关,c,language-lawyer,union,type-punning,C,Language Lawyer,Union,Type Punning,考虑以下代码: 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解释代码的合法性: 对象的存储值只能由左值访问 具有以下类型之一的表达式: -与对象的有效类型兼容的类型 [……] -一个 包含上述类型之一的聚合或联合类型 在其成员中(递归地包括子
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
解释代码的合法性:
对象的存储值只能由左值访问
具有以下类型之一的表达式:
-与对象的有效类型兼容的类型
[……]
-一个
包含上述类型之一的聚合或联合类型
在其成员中(递归地包括子集合的成员
或包含在一起),或
将此应用于上述示例,我们得到:
u->i=10
为u->i
对象提供有效类型int
李>
u
有一个union
类型,其中包含int
类型的成员u->j[0]
可使用具有int
类型成员的union u
类型的左值u
进行访问6.5
我们发现这里没有UB问题:这样的推理正确吗?或者它包含一些错误?是的,您的推理是正确的。这不是未定义的行为,而是根据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节提醒的那样,可移植性问题可能非常严重: 结构或联合对象的值决不是陷阱 表示,即使结构或构件的值 联合对象可以是陷阱表示
陷阱表示法是“不需要表示对象类型值的对象表示法”(定义),理解为“获取陷阱表示法可能执行陷阱,但不需要执行陷阱”(脚注)。因此,访问非活动值可能会导致程序中断,但如果不是这样,那就是无法保证 无论使用何种别名规则,
long
与int
的大小是否相同取决于实现,因此u->j[0]
有访问未初始化字节的风险,从而遇到陷阱值。也就是说,字节具有未指定的值。它并没有说由这些字节表示的对象具有未指定的值。@stantario:不正确。字节可能不是陷阱,但它们形成的值可能是陷阱。在这种情况下,最合适的规则是C 2018 6.5.2.3 3,它说,对于成员访问(使用
或->
),“值是命名成员的值…”正如脚注中告诉我们的,这意味着联合中的字节,如果成员不是最后存储的成员,则重新解释为新类型。据了解,这告诉我们,通过联合使用别名是由C标准定义的(受类型表示的具体实现细节的影响)。我没有关于委员会对此进行审议的信息。1999年标准关于访问成员具有相同的规范性文本,但没有脚注。2007年的草案有脚注。我可能猜,在这两者之间,有一个关于解释的问题,因此添加了脚注,它被认为只是一个澄清,不值得更改规范性文本。@exnihilo感谢您指出!在尼格特的中间,我被工会和指针的相同名字弄糊涂了:“我要编辑这个令人困惑的名字,让我也做了一次双关。”