【问题标题】:Is writing then reading different union members undefined according to the C standard? [duplicate]根据 C 标准,写入然后读取不同的联合成员是否未定义? [复制]
【发布时间】:2019-03-09 00:09:43
【问题描述】:

我读到这段代码根据 c 标准是未定义的,但我找不到原因。 它在 gcc 8.1.0 和 clang-6.0 中编译没有错误并打印 1。

代码如下:

#include <stdio.h>

int main()
{
   union {
     int i;
     short s;
   } u;  
   u.i = 42;
   u.s = 1;

   printf("%d\n", u.i);
   return 0;
}

【问题讨论】:

  • @tadman 这不是未定义的行为。从 C99 开始特别允许,并在 C11 中添加了一个脚注,阐明了它的合法性。
  • @ChristianGibbons 如果您有引用会有所帮助。我看不出你怎么能在这里定义i 会发生什么。
  • @tadman 查看一些程序员老兄的回答。他比我快。
  • @ChristianGibbons 感谢您的来信。拉了我的答案。
  • 根据 6.2.6.1/5 的措辞和脚注 95,它是未定义的除非其中一个成员是字符类型,我将其解释为单个字符或数组。在char 或unsigned char 中没有陷阱表示,因此您应该能够安全地写入T 类型的成员并从char 或char [N] 类型的成员中读取。对于任何其他类型,所有赌注都已取消。

标签: c language-lawyer


【解决方案1】:

来自 C11 规范,§6.5.2.3 注释 95:

如果用于读取联合对象内容的成员与上次用于读取的成员不同 在对象中存储一个值,该值的对象表示的适当部分被重新解释 作为 6.2.6 中描述的新类型中的对象表示(有时称为“类型 双关语'')。这可能是一个陷阱表示。

这表示您正在做的事情是允许的,但也意味着您读取的值可能不是您所期望的(例如通过写入int 成员并从float 成员读取)。

还有一个关于陷阱表示值的警告,在这种情况下,行为将是未定义的。对于two's complement 系统(这是过去几十年所有计算机中的绝大多数),这不是整数值的问题。


在您的情况下,结果将很大程度上取决于平台endianness。要么你会得到你写的值(1),要么你会得到0。

【讨论】:

  • 我可以想象一个带有“负零”的奇异补码系统是一个陷阱。
  • @ChristianGibbons 哦,那些讨厌的深奥平台总是给一切带来麻烦...... :)
  • 有些平台只是想看着世界燃烧。
  • 如果没有硬件支持,one's-complement 使乘法和除法更便宜,如果在软件中实现,也会使 long mul/div 更便宜。
  • 在 powerpc 中打印 65578
【解决方案2】:
union {
    int i;
    short s;
  } u;  
u.i = 42;
u.s = 1;`

当您为u.i 分配的值大于空头可以容纳的值时会发生什么?例如,试试这个:

u.i = 40000;
u.s = 1;

编译器应该在分配short 之前清除为u 保留的整个空间,还是应该只写入存储新值所需的字节?由于您有责任跟踪如何解释存储在 u 中的值,因此存储一种类型然后读取不同大小的不同类型似乎是一个糟糕的计划。

【讨论】:

    【解决方案3】:

    写给工会的一个成员并从另一个成员那里阅读被称为类型双关语,the standard 允许这样做。

    这在第 6.5.2.3 节中有详细说明:

    3 后缀表达式后跟. 运算符和标识符指定结构或联合对象的成员。这 value 是命名成员的值,95) 并且是一个左值,如果 第一个表达式是一个左值。如果第一个表达式 有合格的类型,结果有如此合格的版本 指定成员的类型。

    95) 如果用于读取联合对象内容的成员不是 与上次用于在对象中存储值的成员相同, 值的对象表示的适当部分是 重新解释为新类型中的对象表示 在 6.2.6 中描述(有时称为“类型 punning")。这可能是一个陷阱表示。

    【讨论】:

    • 表达式u.i 是int 类型的左值。根据N1570 6.5p7,联合对象u只能以u的类型或字符类型的左值访问; u.i 两者都不是。据我所知,使用像u.i 这样的非字符类型左值访问u 的能力不是标准的一部分,而只是一个(非常)流行的扩展。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-12
    • 1970-01-01
    • 1970-01-01
    • 2021-09-08
    相关资源
    最近更新 更多