kgr*_*ney 18 c++ unions reinterpret-cast
从其他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;
}
Run Code Online (Sandbox Code Playgroud)
预计此代码的输出使用GCC和clang编译器.
size of int 4
size of float 4
myInt 65535
myFloat 9.18341e-41
theFloat 9.18341e-41
Run Code Online (Sandbox Code Playgroud)
我的问题是,该标准是否真的排除了myFloat确定性的价值?是否以任何方式使用reinterpret_cast 更好的方式来执行此类转换?
该标准在§9.5.1中声明如下:
在并集中,至多一个非静态数据成员可以在任何时间处于活动状态,也就是说,任何时候最多一个非静态数据成员的值都可以存储在并集中.[...] union的大小足以包含其中最大的非静态数据成员.每个非静态数据成员都被分配,就好像它是结构的唯一成员一样.union对象的所有非静态数据成员都具有相同的地址.
保证所有非静态成员具有相同地址的最后一句似乎表明使用联合保证与使用a相同reinterpret_cast,但早期关于活动数据成员的声明似乎排除了这种保证.
那么哪种结构更正确?
编辑:
使用英特尔的icpc编译器,上面的代码产生了更有趣的结果:
$ icpc union.cpp
$ ./a.out
size of int 4
size of float 4
myInt 65535
myFloat 0
theFloat 0
Run Code Online (Sandbox Code Playgroud)
它未定义的原因是因为无法保证究竟是什么值int和float是什么.C++标准并未说a float存储为IEEE 754单精度浮点数.应标准对你说治疗正是int与值对象0xffff的float?除了未定义的事实之外,它没有说什么.
然而,实际上,这是为了reinterpret_cast- 告诉编译器忽略它所知道的关于对象类型的所有内容,并相信它int实际上是一个float.它几乎总是用于机器特定的位级jiggery-pokery.一旦你这样做,C++标准就不能保证你的任何东西.那时,您可以自行了解编译器和机器在这种情况下的作用.
这是双方的真实union和reinterpret_cast方法.我建议reinterpret_cast这个任务"更好",因为它使意图更清晰.但是,保持代码定义良好始终是最好的方法.
这不是未定义的行为.它是实现定义的行为.第一个意味着坏事可能发生.另一个意味着将要发生的事情必须由实施来定义.
reinterpret_cast违反了严格的别名规则.所以我认为它不会可靠地运作.联合技巧是人们所说的type-punning,通常是编译器允许的.gcc人员记录了编译器的行为:http://gcc.gnu.org/onlinedocs/gcc/Structures-unions-enumerations-and-bit_002dfields-implementation.html#Structures-unions-enumerations-and-bit_002dfields-implementation
我认为这也适用于icpc(但它们似乎没有记录它们是如何实现的).但是当我查看程序集时,它看起来像icc试图欺骗浮动并使用更高精度的浮点数.传递-fp-model source给编译器修复了那个.使用该选项,我得到与gcc相同的结果.我认为你一般不想使用这个标志,这只是验证我的理论的一个测试.
所以对于icpc,我认为如果你将代码从int/float切换到long/double,那么type-punning也适用于icpc.