20

他の StackOverflow の質問や、 ISO/IEC ドラフト C++ 標準規格の§9.5.1を読むと、共用体を使用してデータのリテラルを実行することreinterpret_castは未定義の動作であることがわかります。

以下のコードを検討してください。目標は、整数値を取得し、0xffffそれを IEEE 754 浮動小数点の一連のビットとして文字通り解釈することです。( Binary convert は、これがどのように行われるかを視覚的に示しています。 )

#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 で次のように述べています。

共用体では、非静的データ メンバーの最大 1 つがいつでもアクティブになることができます。つまり、非静的データ メンバーの最大 1 つの値をいつでも共用体に格納できます。[...] ユニオンのサイズは、最大の非静的データ メンバーを格納するのに十分です。各非静的データ メンバーは、構造体の唯一のメンバーであるかのように割り当てられます。ユニオン オブジェクトのすべての非静的データ メンバーは、同じアドレスを持ちます。

すべての非静的メンバーが同じアドレスを持つことを保証する最後の文は、共用体の使用が aの使用と同一であることが保証さreinterpret_castれていることを示しているようですが、アクティブなデータ メンバーに関する前のステートメントは、この保証を排除しているようです。

では、どちらの構文がより正しいでしょうか?

編集: Intel の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との値表現が正確に何であるかが保証されていないためfloatです。floatC++ 標準では、 aが IEEE 754 単精度浮動小数点数として格納されるとは規定されていません。int値を持つオブジェクトを0xffffとして扱うことについて、標準は正確に何を言うべきfloatですか? 未定義であるという事実以外は何も言いません。

ただし、実際には、これはreinterpret_castオブジェクトの型について知っていることをすべて無視し、これintが実際にはfloat. ほとんどの場合、マシン固有のビットレベルのジガリー ポケリーに使用されます。C++ 標準は、一度実行すると何も保証しません。その時点で、この状況でコンパイラとマシンが何をするかを正確に理解するのはあなた次第です。

unionこれは、およびreinterpret_castアプローチの両方に当てはまります。reinterpret_cast意図がより明確になるため、このタスクには「より良い」ことをお勧めします。ただし、コードを明確に定義しておくことが常に最善の方法です。

于 2013-05-19T16:59:30.283 に答える
1

未定義の動作は、悪いことが起こらなければならないという意味ではありません。これは、言語定義が何が起こるかを教えてくれなかったことを意味するだけです。この種の駄洒落は、太古の昔から (つまり、1969 年から) C および C++ プログラミングの一部でした。これが機能しないコンパイラを作成するには、特にひねくれた実装者が必要です。

于 2013-05-19T17:04:49.387 に答える