从其他 StackOverflow 问题和阅读ISO/IEC C++ 标准草案的第9.5.1节可以看出,使用联合体进行数据的字面上的reinterpret_cast
是未定义的行为。
考虑下面的代码。目标是将十六进制值0xffff
直接解释为IEEE 754浮点数中的一系列位。(二进制转换可视化地展示了这一过程。)
#include <iostream>
using namespace std;
union unionType {
int myInt;
float myFloat;
};
int main() {
int i = 0xffff;
unionType u;
u.myInt = i;
cout << "size of int " << sizeof(int) << endl;
cout << "size of float " << sizeof(float) << endl;
cout << "myInt " << u.myInt << endl;
cout << "myFloat " << u.myFloat << endl;
float theFloat = *reinterpret_cast<float*>(&i);
cout << "theFloat " << theFloat << endl;
return 0;
}
预期使用GCC和Clang编译器运行此代码将生成输出。
size of int 4
size of float 4
myInt 65535
myFloat 9.18341e-41
theFloat 9.18341e-41
我的问题是,标准实际上是否排除了myFloat
的值是确定性的?使用reinterpret_cast
执行此类型的转换在任何方面上是否更好?标准在§9.5.1中规定如下:
在联合体中,“最多只能有一个非静态数据成员处于活动状态,即,在联合体中最多可以存储一个非静态数据成员的值。”[...]联合体的大小足以包含其非静态数据成员中最大的一个。每个非静态数据成员都被分配为结构体的唯一成员。联合体对象的所有非静态数据成员具有相同的地址。
最后一句保证了所有非静态成员具有相同的地址,这似乎表明使用联合体与使用
reinterpret_cast
是保证相同的,但早期关于活动数据成员的声明似乎排除了此保证。那么哪种结构更正确?
编辑: 使用英特尔的
icpc
编译器,上述代码产生了更有趣的结果:$ icpc union.cpp
$ ./a.out
size of int 4
size of float 4
myInt 65535
myFloat 0
theFloat 0
uint32_t x; *(float*)(&x) = 1.5;
一样是错误的。将一个对象解释为一系列字节的正确方法是将其视为char[]
。 - Kerrek SB