C++ unions vs. reinterpret_cast
C++ unions vs. reinterpret_cast
从其他StackOverflow问题和阅读ISO/IEC c++标准草案& section;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
是否更好?
标准在& section;9.5.1中声明如下:
在union中,在任何时候最多只能有一个非静态数据成员处于活动状态,也就是说,在任何时候最多只能有一个非静态数据成员的值存储在union中。[…union的大小足以包含其最大的非静态数据成员。分配每个非静态数据成员就好像它是结构体的唯一成员一样。联合对象的所有非静态数据成员具有相同的地址。
最后一句话,保证所有非静态成员有相同的地址,似乎表明联合的使用保证与reinterpret_cast
的使用相同,但前面关于活动数据成员的语句似乎排除了这种保证。
那么哪个结构更正确呢?
编辑:使用英特尔的icpc
编译器,上面的代码产生了更有趣的结果:
$ icpc union.cpp
$ ./a.out
size of int 4
size of float 4
myInt 65535
myFloat 0
theFloat 0
未定义的原因是无法保证int
和float
的值表示究竟是什么。c++标准并没有说float
被存储为IEEE 754单精度浮点数。对于将值为0xffff
的int
对象处理为float
,标准应该怎么说?它没有说明任何东西,除了它是未定义的事实。
实际上,这就是reinterpret_cast
的目的——告诉编译器忽略它所知道的关于对象类型的一切,并相信这个int
实际上是一个float
。它几乎总是用于特定于机器的位级欺诈。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,类型双关也会在icpc上工作。
未定义的行为并不意味着坏事一定会发生。这意味着只有,语言定义没有告诉您发生了什么。这种类型的双关自远古以来(即自1969年以来)一直是C和c++编程的一部分;只有特别反常的实现者才会编写出这样的编译器
- 如何理解C++标准N3337中的expr.const.cast子句8
- C++Cast运算符过载
- 错误:"cast"未命名类型void setCastDescription(std::string
- 通过使用 const-cast 的非常量引用来延长临时的寿命
- "(void) cast"与功能有什么区别 "__attributes__"来沉默未使用的参数警告?
- C++:"Expected '(' for function-style cast or type construction"错误
- 为什么选择 g++ 给予者:"error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]"
- Gtk+ g_signal_connect() 和 C++ lambda 会导致"invalid cast"错误
- Shared_ptr cast vs static_cast speed
- C++20 中的严格别名规则是否允许标准 c++ unicode 字符和下划线类型之间"reinterpret
- 在 iOS 上使用 Aruco 构建 OpenCV 时"Functional-style cast from id to double is not allowed"
- 覆盖 CAST 运算符(我认为它被称为向下转换)
- C++错误,隐 <function-style-cast> 式要求使用模板化类一次调用多个构造函数的多个转换
- 如何修复<function-style-cast>错误:无法从'initializer list'转换为asdending比较<W>(模板函子)
- C++ C++ 中的函数声明,键入 CAST
- static_cast会丢弃错误,但C风格的演员cast有效
- C++ cast char * to unsigned char
- 禁用 Clang 中的"cast from pointer to smaller type uint32_t"错误
- 是否可以使用gcc 3.3版修复与int*cast相关的Sun Solaris OS 5.8分段故障
- C++ const-cast 一个引用