2017-04-13 4 views
1

たとえば、私はバイト配列仮想関数を持つ構造体をバイト配列にキャストすることは安全ですか?

uint8_t data[3];

を持つ構造定義

struct Data { 
    uint8_t data1; 
    uint16_t data2; 
    virtual uint8_t getData1() { return data1; } 
    virtual uint16_t getData2() { return data2; } 
} 

を持っているが、これを行うには、それが安全である:

Data *d = (Data*)data;

私が読んで、ので、私は求めています仮想関数を持つクラスは 仮想テーブルポインタを格納し、それを定義する標準はありません はstorですオブジェクト内のed。また、私はインスタンス

struct Data2 : Data { uint8_t data3; 
virtual uint8_t getData3() { return data3; } }

のために、データを継承した場合のData2 のオブジェクトのメンバ変数が格納されている順序できますか?バイト配列を介してData2構造体をキャストすると、 のdata1、data2、data3の順序になりますか?前もって感謝します。

+1

アラインメントの問題を起こしているので、 'virtual'に関係なく安全ではありません。(' struct data {uint8_t data1; uint16_t data2;} 'はほとんどの場合、サイズは4ではなく3です。 – melpomene

+0

好ましい方法は、バッファから構造体にメンバーを個別に割り当てることです。これにより、アラインメントとエンディアンの問題を処理できます。 –

+0

申し訳ありませんが、ストレージアライメントについて言及していません。 #pragma(pack、1)のようなアライメントマクロを使用しても、構造内の仮想関数で安全に使用できますか? –

答えて

4

C++では、c style castは同等のC++キャストと解釈されますが、キャストを達成できる最も制限的なものです。この場合は、reinterpret_castです。

reinterpret_castのドキュメントには、定義されたすべてのユースケースが列挙されています。残念ながら、あなたのケースでは、結果のポインタを参照解除することは禁じられています。バーチャルメソッドの存在はこれに関係しません。

反対を行い、Data *uint8_t *にキャストすることは合法です。このようなuint8_t*Data*に戻すことも合法です。

編集:Dataのインスタンスにストレージを提供することを目的としている場合は、std::aligned_storageplacement newを使用できます。 std::aligned_storageは、タイプのインスタンスを構築できる安全なメモリロケーションを提供し、配置newでは、インスタンスを構築する場所を指定できます。ただし、派生型を格納する場合はうまく機能しません。

+0

私はあなたが禁じられたと言って、実際にキャストすることができたので、実際には混乱しています。 –

+0

私たちは、ネットワークからのものでも、ファイルからのものでもよいバイトのストリームを持っています。現在、このようなキャストを行い、データを取得しています。私の目的は、現在のプロセスを簡素化し、拡張と維持を容易にすることです。新しいバージョン(例えば、より多くのメンバを持つ構造体)が存在するか、後方互換性を保つかどうか。 –

+0

プラグマパックを使用して構造全体をアロインします。 –

1

通常、データ構造体へのバイト配列のキャストが発生すると、この構造体のバイナリレイアウトを保証する開発者の責任があります。あなたの例では、バイナリレイアウトはかなり変わる可能性があり、vtableの存在は問題の1つに過ぎません。別の問題は、フィールドのアラインメントです。これは通常、コンパイルオプションに依存します。たとえば、配置が4バイトの場合、構造の側面は少なくとも3バイトの配列に収まらない少なくとも8バイト+ vtable関連のポインタになります。この場合キャストやディープコピーを行うと深刻な問題になります。

構造体のサイズは、あなたがこのように、#pragma packまたは類似の構築物および静的アサーションを使用することができます正しいことを確認するために:

#pragma pack(push, 1) // make sure that fields are packed 

struct Data { 
uint8_t data1; 
uint16_t data2; 
}; 

#pragma pack(pop) // restore initial alignment settings 

static_assert(sizeof(Data) == 3, "Data struct layout is not correct"); 

もう一つの問題は、データへのポインタがに許可されていないため、未定義の動作を爆発strict aliasing rules、ありますalias uint8_tへのポインタ。

Data *d = reinterpret_cast< Data * >(reinterpret_cast<::std::uintptr_t>(data)); 

そして、さらに別の問題は、アレイで書かれたuint16_tフィールドの異なるエンディアンかもしれないが、残念ながら:だから、この場合には、ダブルキャスト(あるいは深いコピーが)コンパイラは、ポインタについてあまりにも多くの仮定をすることはありませんので、必要とされますそれに対処するストレートな方法はありません。

関連する問題