20

其他 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 中规定了以下内容:

在一个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
4

3 回答 3

9

int它未定义的原因是因为无法保证and的值表示究竟是什么float。C++ 标准没有说 afloat存储为 IEEE 754 单精度浮点数。int关于您将具有 value 的对象0xffff视为 a ,该标准究竟应该说些什么float?除了它是未定义的事实之外,它什么也没说。

然而,实际上,这是reinterpret_cast- 告诉编译器忽略它所知道的关于对象类型的所有内容并相信你这int实际上是一个float. 它几乎总是用于特定于机器的位级跳棋。一旦你这样做了,C++ 标准就不能保证任何事情。此时,您需要准确了解编译器和机器在这种情况下的作用。

对于 theunionreinterpret_cast方法都是如此。我建议这reinterpret_cast对这项任务“更好”,因为它使意图更清晰。但是,保持代码定义明确始终是最好的方法。

于 2013-05-19T16:59:30.283 回答
7

这不是未定义的行为。它是实现定义的行为。第一个确实意味着可能会发生坏事。另一个意味着将要发生的事情必须由实现来定义。

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 上工作。

于 2013-05-19T17:34:34.813 回答
1

未定义的行为并不意味着坏事必须发生。这意味着语言定义不会告诉您会发生什么。这种类型的双关语自远古时代(即自 1969 年以来)就已成为 C 和 C++ 编程的一部分;编写一个不起作用的编译器需要一个特别反常的实现者。

于 2013-05-19T17:04:49.387 回答