2009-02-21 15 views
331

私はreinterpret_caststatic_castの適用性について少し混乱しています。コンパイル時にタイプを解釈できるので、一般的な規則はstaticキャストを使用しているので、単語staticを読みました。これは、C++コンパイラが暗黙のキャストのために内部的に使用するキャストです。reinterpret_castをいつ使用しますか?

reinterpret_castは、整数型をポインタ型に変換したり、ポインタ型を別の型に変換したりする2つのシナリオに適用できます。私が得る一般的な考えは、これは不可能であり、避けるべきです。

私が少し混乱しているのは、私が必要とする使い方です.C++をCから呼び出しています.CコードはC++オブジェクトを保持する必要がありますので、基本的にはvoid*を保持しています。 void *とクラスの間の変換にはどのようなキャストを使用する必要がありますか?

私はstatic_castreinterpret_castの両方の使用法を見ていますか?私が読んできたことから、キャストはコンパイル時に起こるので、staticが優れていますか?あるポインタ型から別のポインタ型に変換するのに、reinterpret_castを使用すると言いますか?

+2

'reinterpret_cast'は実行時には発生しません。どちらもコンパイル時のステートメントです。 http://en.cppreference.com/w/cpp/language/reinterpret_castから: "const_castとは異なり、const_castのように、reinterpret_cast式はCPU命令にコンパイルされません。これは純粋にコンパイラ指示文ですあたかもそれがnew_type型を持っているかのように、表現のビット(オブジェクト表現)のシーケンスを扱う " –

+0

@HeretoLearn、* .cと* .cppファイルから関連コードを追加することは可能ですか?私はそれが質問の説明を改善できると思います。 – OrenIshShalom

答えて

335

C++標準では次のことを保証:へとアドレスを保存しvoid*からポインタをINGの

static_cast。それは、次のように、同じアドレスへのすべてのポイントa、b、cのである:

int* a = new int(); 
void* b = static_cast<void*>(a); 
int* c = static_cast<int*>(b); 

reinterpret_castのみ保証つまり、あなたは別の種類、へのポインタをキャストした場合、その後reinterpret_cast、それを元の形に、元の値を取得します。したがって、次のようになります。

aとcは同じ値を含みますが、bの値は指定されていません。 (実際には、通常はaとcと同じアドレスが含まれますが、標準では指定されていません。さらに複雑なメモリシステムを持つマシンでは当てはまらないかもしれません。)

void *とのキャストの場合は、static_castが好ましいはずです。

+16

私は 'b'が定義されていないことが好きです。あなたはそれで愚かなことをやめます。別のポインタ型に何かをキャストすると、問題を尋ねてきます。それに依存できないという事実により、あなたはもっと注意深くなります。とにかく 'b'は何の上にstatic_cast <>を使用したのですか? –

+2

私はreinterpret_cast <>が同じビットパターンを保証していると思いました。 (これは他の型への有効なポインタと同じではありません)。 –

+1

@Martin - reinterpret_cast <>は、同じビットパターンになるとは限りません。 "reinterpret_cast <>によって実行されるマッピングは実装定義です。" (C++ 03 5.3.10)。しかし、この規格は「それは意外ではない」と述べている。 –

-14

FAQをお読みください! C言語でのC++データの保持は危険です。

C++では、オブジェクトへのポインタをキャストなしでvoid *に変換できます。しかしそれは他の方法では真実ではありません。元のポインタを元に戻すには、static_castが必要です。

16

reinterpret_castの意味は、C++標準では定義されていません。したがって、理論的にはreinterpret_castがプログラムをクラッシュさせる可能性があります。実際には、コンパイラはあなたが予想していることをしようとします。これは、渡しているもののビットを、あなたがキャストしている型であるかのように解釈することです。あなたが使用しようとしているコンパイラがreinterpret_castであることを知っていればそれを使うことができますが、それは移植可能なであると言います。

あなたが説明したケースでは、reinterpret_castと思われるかもしれませんが、代わりにstatic_castなどの代替方法を使用できます。とりわけ標準は、これはあなたがstatic_cast(§5.2.9)に期待できるかについて述べている:あなたのユースケースのためにそう

An rvalue of type “pointer to cv void” can be explicitly converted to a pointer to object type. A value of type pointer to object converted to “pointer to cv void” and back to the original pointer type will have its original value.

、それはあなたがstatic_castを使用するための標準化委員会が意図したことをかなり明確なようです。

+5

プログラムがクラッシュすることはありません。この規格は、reinterpret_castについていくつかの保証を提供しています。人々がしばしば期待しているほど多くはありません。 – jalf

+0

さて、reinterpret_cast自体はおそらくクラッシュしませんでしたが、使用しようとするとクラッシュする可能性があるという偽の結果を返す可能性があります。 – flodin

+1

正しく使用しない場合。つまり、AからBへのreinterpret_castは完全に安全で明確に定義されています。しかし、Bの価値は不明です、そして、もしあなたがそれに頼るなら、悪いことが起こる可能性があります。しかし、キャスト自体は、あなたがスタンダードが許す方法でしか使用しない限り、十分安全です。 ;) – jalf

115

reinterpret_castが必要な場合は、不透明データ型とインタフェースするときです。これはプログラマーが制御できないベンダーAPIで頻繁に発生します。ここでは、ベンダーが任意のグローバルデータを格納し、取得するためのAPIを提供して不自然な例です:

// vendor.hpp 
typedef struct _Opaque * VendorGlobalUserData; 
void VendorSetUserData(VendorGlobalUserData p); 
VendorGlobalUserData VendorGetUserData(); 

このAPIを使用するには、プログラマは再びVendorGlobalUserDataとバックにデータをキャストする必要があります。 static_castが動作しません、一つはreinterpret_castを使用する必要があります。

以下
// main.cpp 
#include "vendor.hpp" 
#include <iostream> 
using namespace std; 

struct MyUserData { 
    MyUserData() : m(42) {} 
    int m; 
}; 

int main() { 
    MyUserData u; 

     // store global data 
    VendorGlobalUserData d1; 
// d1 = &u;           // compile error 
// d1 = static_cast<VendorGlobalUserData>(&u);  // compile error 
    d1 = reinterpret_cast<VendorGlobalUserData>(&u); // ok 
    VendorSetUserData(d1); 

     // do other stuff... 

     // retrieve global data 
    VendorGlobalUserData d2 = VendorGetUserData(); 
    MyUserData * p = 0; 
// p = d2;           // compile error 
// p = static_cast<MyUserData *>(d2);    // compile error 
    p = reinterpret_cast<MyUserData *>(d2);   // ok 

    if (p) { cout << p->m << endl; } 
    return 0; 
} 

は、サンプルAPIの不自然な実装です:

// vendor.cpp 
static VendorGlobalUserData g = 0; 
void VendorSetUserData(VendorGlobalUserData p) { g = p; } 
VendorGlobalUserData VendorGetUserData() { return g; } 
+5

これは、私が考えることができるreinterpret_castの唯一の意味のある使用についてです。 – jalf

+7

これは遅れている質問かもしれませんが、なぜベンダーAPIがそれに 'void *'を使用していませんか? – Xeo

+76

@XeoベンダーAPIが吸うので。 –

0
template <class outType, class inType> 
outType safe_cast(inType pointer) 
{ 
    void* temp = static_cast<void*>(pointer); 
    return static_cast<outType>(temp); 
} 

私が結論しようとしたテンプレートを使用して簡単な安全なキャストを書きました。 このソリューションは、関数にポインタをキャストすることを保証しません。

+0

何ですか?なぜ迷惑?これは、正確には、この状況でreinterpret_castが既に行っていることです: "オブジェクトポインタを別の型のオブジェクトポインタに明示的に変換することができます。[72] オブジェクトポインタ型の_prvalue_' v'をオブジェクトポインタに変換すると"static_cast (static_cast (v))'となります。 - N3797。 –

+0

'C++ 2003'の標準については、' reinterpret_cast'は 'static_cast (static_cast (v))' –

+0

を確認できません。ほとんどのコーダーは、(可能性が高いので)避けることができない場合もありません。回答とコメントは、別段の指定がない限り、最新の利用可能な標準を反映しているはずです... IMHO。とにかく、委員会は2003年以降明示的にこれを追加する必要があると感じたと思う。(IIRCのため、C++ 11と同じだった) –

9

1つの使用は、あなたが(IEEE 754)にビット演算を適用したい場合は浮いています。これは、整数としてフロートのバイナリ表現を扱い、それが右シフトと定数からそれを減算し、それによって指数を半分にし、否定

https://en.wikipedia.org/wiki/Fast_inverse_square_root#Overview_of_the_code

:この一例は、高速逆平方根トリックました。

これは、もともとCで書かれた
float Q_rsqrt(float number) 
{ 
    long i; 
    float x2, y; 
    const float threehalfs = 1.5F; 

    x2 = number * 0.5F; 
    y = number; 
    i = * (long *) &y;      // evil floating point bit level hacking 
    i = 0x5f3759df - (i >> 1);    // what the deuce? 
    y = * (float *) &i; 
    y = y * (threehalfs - (x2 * y * y)); // 1st iteration 
// y = y * (threehalfs - (x2 * y * y)); // 2nd iteration, this can be removed 

    return y; 
} 

ので、Cはキャスト使用していますが、類似したC++のキャストはreinterpret_castは次のとおりです。floatに戻って変換した後、より正確なこの近似を行うためにニュートン・ラプソン反復を受けています。

+1

'エラー:タイプ 'int64_t {aka long long int}'のrvalue式の無効なキャスト reinterpret_cast (reinterpret_cast (d) >> 1)+(1L << 61)) - http://ideone.com/6S4ijc – Orwellophile

+0

申し訳ありませんが、私は(非常に混濁している)メモリからコードを再現しようとしていました。これで正しいオリジナルのコードに置き換えられました。 –

+0

標準では、これは未定義の動作であると言われています。http://en.cppreference.com/w/cpp/language/reinterpret_cast( "type aliasing"の下) –

-3

クイックアンサー:コンパイルする場合はstatic_castを、そうでない場合はreinterpret_castを使用してください。

0

まずあなたがここにint型のような特定のタイプでいくつかのデータを持っている:

int x = 0x7fffffff://==nan in binary representation 

次にあなたがフロートのような他のタイプと同じ変数にアクセスしたい: あなたは

float y = reinterpret_cast<float&>(x); 

//this could only be used in cpp, looks like a function with template-parameters 

間を決めることができます

又は

float y = *(float*)&(x); 

//this could be used in c and cpp 

簡単:それは同じメモリが使用されることを意味します別の種類として。だから、上記のようなint型としてfloatのバイナリ表現をfloatに変換することができます。例えば0x80000000は-0です(仮数と指数はnullですが、符号msbは1です。これは倍精度と長い倍精度でも有効です)。

最適化:私はreinterpret_castが多くのコンパイラで最適化されると思いますが、Cキャストはポインタ算術によって行われます(値はメモリにコピーする必要があり、ポインタはCPUレジスタを指すことができません)。

注:いずれの場合も、キャストされる前にキャストされた値を変数に保存する必要があります。このマクロは助けることができる:

#define asvar(x) ({decltype(x) __tmp__ = (x); __tmp__; }) 
26

を短い答え: あなたがそれを使用しないでください、reinterpret_castは何の略かわからない場合。あなたが将来それを必要とするなら、あなたは知っているでしょう。

完全な答えは:

のが基本的な数値型を考えてみましょう。

たとえば、int(12)unsigned float (12.0f)に変換すると、両方の数値が異なるビット表現を持つため、プロセッサが計算を呼び出す必要があります。これは、static_castの略です。

一方、reinterpret_castを呼び出すと、CPUは計算を実行しません。それはちょうどそれが別のタイプを持っているかのようにメモリのビットのセットを扱います。したがって、このキーワードでint*float*に変換すると、新しい値(ポインタのデフレの後)は数学的意味での古い値とは関係ありません。

例:reinterpret_castが1つの理由で移植できないことは事実である - バイト順(エンディアン)。しかし、これは驚くべきことに、しばしばそれを使う最善の理由です。この例を想像してみましょう:ファイルからバイナリ32ビットの数値を読み取る必要があり、ビッグエンディアンであることがわかります。コードは汎用的でなければならず、ビッグエンディアン(ARMなど)やリトルエンディアン(x86など)システムでも適切に動作します。バイトオーダーをチェックする必要があります。あなたがconstexpr関数を書くことができますので、それはコンパイル時にはよく知られている:

constexpr bool is_little_endian() { 
    unsigned short x=0x0001; 
    auto p = reinterpret_cast<unsigned char*>(&x); 
    return *p != 0; 
} 

説明:メモリ内xのバイナリ表現は0000'0000'0000'0001(大)または0000'0001'0000'0000(リトルエンディアン)である可能性があります。再解釈キャスト後、pポインタの下のバイトは、それぞれ0000'0000または0000'0001になります。静的キャスティングを使用している場合、どのエンディアンが使用されていても、常に0000'0001になります。

関連する問題