【发布时间】:2020-04-28 20:01:39
【问题描述】:
考虑以下代码:
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。
问题:这样的推理是否正确?或者它包含一些错误?
【问题讨论】:
-
不管别名规则如何,
long是否与int大小相同取决于实现,因此u->j[0]存在访问未初始化字节从而遇到陷阱值的风险。 -
这表示 bytes 采用未指定的值。它并没有说这些字节表示的对象采用未指定的值。
-
@stantario:不正确。字节可能不是陷阱,但它们形成的值可以。
-
在这种情况下适用的规则是 C 2018 6.5.2.3 3,它表示,对于成员访问(使用
.或->),“值是命名成员...”正如脚注告诉我们的那样,这意味着如果该成员不是最后存储的成员,则联合中的字节将被重新解释为新类型。这被理解为告诉我们通过联合的别名是由 C 标准定义的(受类型表示方式的特定于实现细节的影响)。 -
我不知道委员会对此的审议情况。 1999 年标准具有相同的关于访问成员的规范性文本,但没有脚注。 2007 年的草案有脚注。我可能猜想,两者之间存在解释问题,因此添加了脚注,认为只是澄清,不值得更改规范性文本。
标签: c language-lawyer union type-punning