12

reinterpret_castchar*(またはchar[N])の場合、定義された動作はどのような場合ですか?私はこの質問に答えるために使用すべき親指のルールは何ですか?reinterpret_cast、char *、および未定義の動作


我々はthis questionから学んだように、次は未定義の動作です:

alignas(int) char data[sizeof(int)]; 
int *myInt = new (data) int;   // OK 
*myInt = 34;       // OK 
int i = *reinterpret_cast<int*>(data); // <== UB! have to use std::launder 

しかし、どの時点で我々はcharアレイ上reinterpret_castを行うと、それは未定義の動作にすることはできませ持つことができますか? int開始のために生涯をしたとき

alignas(int) char data[sizeof(int)]; 
*reinterpret_cast<int*>(data) = 42; // is the first cast write UB? 
int i = *reinterpret_cast<int*>(data); // how about a read? 
*reinterpret_cast<int*>(data) = 4;  // how about the second write? 
int j = *reinterpret_cast<int*>(data); // or the second read? 

  1. new、ちょうどreinterpret_cast:ここではいくつかの簡単な例ですか?それはdataの宣言とそれですか?もしそうなら、寿命はいつ終わるのですかdata

  2. ポインタがdataの場合はどうなりますか?

    char* data_ptr = new char[sizeof(int)]; 
    *reinterpret_cast<int*>(data_ptr) = 4;  // is this UB? 
    int i = *reinterpret_cast<int*>(data_ptr); // how about the read? 
    
  3. 私はちょうどワイヤ上の構造体を受信し、条件付きで最初のバイトが何であるかに基づいて、それらをキャストしたいんですか?

    // bunch of handle functions that do stuff with the members of these types 
    void handle(MsgType1 const&); 
    void handle(MsgTypeF const&); 
    
    char buffer[100]; 
    ::recv(some_socket, buffer, 100) 
    
    switch (buffer[0]) { 
    case '1': 
        handle(*reinterpret_cast<MsgType1*>(buffer)); // is this UB? 
        break; 
    case 'F': 
        handle(*reinterpret_cast<MsgTypeF*>(buffer)); 
        break; 
    // ... 
    } 
    

これらのケースのUBのいずれかいますか?それらのすべてですか?この質問に対する答えはC++ 11からC++ 1zに変更されますか?

+0

**(1)**は有効です。どちらのステートメントでも、新しい 'int'オブジェクトが作成され、値が割り当てられます。 *読んで*その価値は物事が毛むくじゃくするところです。 **(2)**と同じです( 'sizeof(int)== 4'と仮定します)。 **(3)**は私にUBのように見えます。 –

+0

@IgorTandetnikあまりにも読んで質問を洗い出し、 'sizeof(int)'についての前提をなくしました。ありがとう。 – Barry

+1

** **(1)**と**(2)**はリンクされた質問と同じ理由でUBを表示するようです。最初のキャストからポインタを保存し、後続のすべての書き込みと読み取りにポインタを使用することで、簡単に回復できます。 –

答えて

3

ここで遊んで二つの規則があります。

  1. [basic.lval]/8、別名、厳格なエイリアシング規則:簡単に言えば、あなたはへのポインタ/参照を通じてオブジェクトにアクセスすることはできませんが間違ったタイプです。

  2. [base.life]/8:単純に言えば、別のタイプのオブジェクトにストレージを再利用すると、最初に洗濯することなく古いオブジェクトへのポインタを使用することはできません。

これらのルールは、「メモリの場所」または「記憶域」と「オブジェクト」を区別するための重要な部分です。

あなたのコード例のすべてが同じ問題に餌食になる:タイプchar[sizeof(int)]のオブジェクトを作成します

alignas(int) char data[sizeof(int)]; 

:彼らはあなたがそれらをキャストするオブジェクトはありません。そのオブジェクトはではなく、であり、intではありません。したがって、そうでないかのようにアクセスすることはできません。それが読み書きかどうかは関係ありません。あなたはまだUBを挑発します。

同様:

char* data_ptr = new char[sizeof(int)]; 

もタイプchar[sizeof(int)]のオブジェクトを作成すること。

char buffer[100]; 

これはタイプchar[100]のオブジェクトを作成します。そのオブジェクトはMsgType1でもMsgTypeFでもありません。あなたはそれがどちらかと同じようにアクセスすることはできません。

ここでUBとは、最初のバイトを確認したときではなく、Msg*タイプの1つとしてバッファにアクセスしたときです。あなたのMsg*タイプがすべてコピー可能であれば、最初のバイトを読んでから、適切なタイプのオブジェクトにバッファをコピーするのはまったく問題ありません。

switch (buffer[0]) { 
case '1': 
    { 
     MsgType1 msg; 
     memcpy(&msg, buffer, sizeof(MsgType1); 
     handle(msg); 
    } 
    break; 
case 'F': 
    { 
     MsgTypeF msg; 
     memcpy(&msg, buffer, sizeof(MsgTypeF); 
     handle(msg); 
    } 
    break; 
// ... 
} 

ここでは、言語状態が未定義の動作であることについて説明しています。コンパイラがこれらのどれかでうまくいくのは良いことです。

この質問に対する回答はC++ 11からC++ 1zに変更されますか?

C++ 11(特に[basic.life])ので明確いくつかの重要なルールがありました。しかし、ルールの背後にある意図は変わっていません。

+0

私の 'char'配列を宣言しても、空に初期化された' T'型の記憶域を得ることを潜在的には構成していませんか?その意味で、「洗濯」の健康的な散水は、すべてを明確に定義しないだろうか? – Barry

+0

@Barry:[これは 'std :: launder'のためのものではありません](http://stackoverflow.com/a/39382728/734069)。古いオブジェクトを格納しているオブジェクトの存続期間を開始すると、古いオブジェクトへのポインタから新しいオブジェクトへのポインタを取得できます。それは何の生涯も始まらない。 "*私のchar配列を宣言しても、空に初期化されていない型Tのストレージを得ることを潜在的には構成していないのではないでしょうか?*"そのロジックによって、*まだオブジェクトは*まだ空に初期化された型T "である。結局のところ、オブジェクトはストレージを持っています。 'char [X]'は他のオブジェクトと同じくらいのオブジェクトです。 –

+0

しかし、それは、[basic.life]がオブジェクトの存続期間が始まったとき、つまりストレージが獲得されたときと言います。与えられた '' char buf [4]; int * i = new(buf)int; '、' int'の生涯は 'i 'で始まるのはいつですか? – Barry

関連する問題