【问题标题】:Type punning in C using union使用 union 在 C 中键入双关语
【发布时间】: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说明代码的合法性:

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

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

[...]

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

将此应用到上面的示例中:

  1. u->i = 10; 使 u->i 对象具有有效类型 int
  2. 左值u 有一个union 类型,其中包含int 类型的成员。
  3. 具有未指定值的对象 u->j[0] 使用具有 int 类型成员的 union u 类型的左值 u 访问。
  4. 引用 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


【解决方案1】:

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

当一个值存储在联合类型对象的成员中时, 不对应的对象表示的字节 成员,但确实对应于其他成员,取未指定的值。

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

未指定值:相关类型的有效值,其中本国际标准不要求该值是 在任何情况下选择

附件 J:可移植性问题

中提醒了这一点

J.1 未指明的行为
1 以下未指定:
— ...
— 在结构中存储值时填充字节的值或 工会(6.2.6.1)。
— union 对应的字节值 除了最后存储到 (6.2.6.1) 中的成员之外的成员。
— ...

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

话虽如此,移植性问题可能很严重,正如第 6.2.6.1/6 节所提醒的那样:

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

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

【讨论】:

  • @exnihilo 感谢您的指出!在半夜,我对联合和指针的相同命名感到困惑:)我将编辑
  • 这个令人困惑的命名也让我做了双重考虑;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-09-28
  • 1970-01-01
  • 1970-01-01
  • 2020-04-23
  • 2011-09-16
  • 2013-09-27
  • 1970-01-01
相关资源
最近更新 更多