我读到这段代码根据 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("%dn", u.i);
return 0;
}
来自 C11 规范,§6.5.2.3 注释 95:
如果用于读取联合对象内容的成员与上次用于读取的成员不同 在对象中存储一个值,重新解释该值的对象表示的适当部分 作为新类型中的对象表示形式,如 6.2.6 中所述(该过程有时称为"类型" 双关语"(。这可能是陷阱表示形式。
这表示您正在执行的操作是允许的,但也意味着您读取的值可能不是您所期望的值(例如,通过写入int
成员并从float
成员读取(。
还有一个关于陷阱表示值的警告,在这种情况下,行为将是未定义的。对于二进制的补码系统(这是过去几十年所有计算机的绝大多数(,这不是整数值的问题。
在您的情况下,结果将在很大程度上取决于平台字节序。要么你会得到你写的值(1
(,要么你会得到0
。
union { int i; short s; } u; u.i = 42; u.s = 1;`
当您为u.i
分配一个大于空头可以容纳的值时会发生什么?例如,试试这个:
u.i = 40000;
u.s = 1;
编译器应该在分配短值之前清除为u
保留的整个空间,还是应该只写入存储新值所需的字节?由于您有责任跟踪如何解释存储在u
中的值,因此存储一种类型然后读取不同大小的不同类型的值似乎是一个糟糕的计划。
写给工会的一个成员并从另一个成员那里读取被称为类型双关语,这是标准允许的。
这在第 6.5.2.3 节中有详细说明:
3后缀表达式后跟
.
运算符和标识符,用于指定结构或联合对象的成员。 这 值是命名成员的值95(,并且是左值,如果 第一个表达式是左值。 如果第一个表达式 具有限定类型,结果具有所谓的限定版本 指定成员的类型。95(如果用于读取联合对象内容的成员不是 与上次用于在对象中存储值的成员相同, 对象表示值的适当部分是 在新类型中重新解释为对象表示形式 在 6.2.6 中描述(该过程有时称为"类型 双关语"(。 这可能是陷阱表示形式。