union
最後の1セット以外のメンバーにアクセスするのはUB であるという印象を受けましたが、確実な参照が見つからないようです(UBであると主張するが、標準からのサポートがないという回答以外)。
それで、それは未定義の振る舞いですか?
union
最後の1セット以外のメンバーにアクセスするのはUB であるという印象を受けましたが、確実な参照が見つからないようです(UBであると主張するが、標準からのサポートがないという回答以外)。
それで、それは未定義の振る舞いですか?
回答:
混乱は、Cが共用体を介した型パンニングを明示的に許可するのに対し、C ++(c ++ 11)にはそのような許可はありません。
6.5.2.3構造と組合員
95)ユニオンオブジェクトの内容を読み取るために使用されたメンバーが、オブジェクトに値を格納するために最後に使用されたメンバーと同じでない場合、値のオブジェクト表現の適切な部分が新しいオブジェクト表現として再解釈されます6.2.6で説明されているタイプ(「タイプパニング」と呼ばれることもあるプロセス)。これはトラップの表現かもしれません。
C ++の状況:
9.5ユニオン[class.union]
ユニオンでは、最大で1つの非静的データメンバーをいつでもアクティブにできます。つまり、最大で1つの非静的データメンバーの値をいつでもユニオンに格納できます。
C ++にはstruct
、共通の初期シーケンスを持つsを含む共用体の使用を許可する言語が後であります。ただし、これは型パンニングを許可しません。
C ++で共用体型パンニングが許可されているかどうかを判断するには、さらに検索する必要があります。それを思い出しますc99 は、C ++ 11の規範的な参照です(C99はC11と同様の言語で、共用体型パンニングを許可しています)。
3.9タイプ[basic.types]
4-T型のオブジェクトのオブジェクト表現は、T型のオブジェクトによって取り込まれたN個のunsigned charオブジェクトのシーケンスであり、Nはsizeof(T)に等しい。オブジェクトの値表現は、T型の値を保持するビットのセットです。自明なコピーが可能なタイプの場合、値表現は、値を決定するオブジェクト表現のビットのセットです。これは、実装の1つの個別の要素です。定義された値のセット。42
42)C ++のメモリモデルはISO / IEC 9899プログラミング言語Cのメモリモデルと互換性があるという意図があります。
読むと特に面白くなる
3.8オブジェクトの有効期間[basic.life]
タイプTのオブジェクトの存続期間は、次の場合に始まります。—タイプTの適切な配置とサイズのストレージが取得された場合—オブジェクトに重要な初期化がある場合、その初期化は完了です。
したがって、ユニオンに含まれるプリミティブ型(これはipso factoで簡単な初期化が行われます)の場合、オブジェクトの存続期間は少なくともユニオン自体の存続期間を含みます。これにより、
3.9.2複合型[basic.compound]
タイプTのオブジェクトがアドレスAにある場合、値がどのように取得されたかに関係なく、値がアドレスAであるタイプcv T *のポインターはそのオブジェクトを指すと言われます。
私たちが関心を持っている操作が型パンニング、つまり非アクティブなユニオンメンバーの値を取ると仮定し、上記のように、そのメンバーによって参照されるオブジェクトへの有効な参照があるとすると、その操作は左辺値-rvalue変換:
4.1左辺値から右辺値への変換[conv.lval]
非関数、非配列型
T
のglvalueは、prvalueに変換できます。T
が不完全な型である場合、この変換を必要とするプログラムの形式は正しくありません。glvalueが参照するオブジェクトがタイプのオブジェクトでT
はなく、から派生したタイプT
のオブジェクトでもない場合、またはオブジェクトが初期化されていない場合、この変換を必要とするプログラムは未定義の動作をします。
次に問題は、非アクティブな共用体メンバーであるオブジェクトが、ストレージによってアクティブな共用体メンバーに初期化されるかどうかです。私の知る限り、これは事実ではありません。
char
配列ストレージにコピーされて戻されます(3.9:2)、または非アクティブなメンバによって組合へのアクセスが定義されているオブジェクトと値表現に追従するように定義され、上記interpositionsの一つせずにアクセスが未定義の動作です。もちろん、実装では未定義の動作は発生しないと想定しているため、これはそのようなプログラムで実行できる最適化に影響を与えます。
つまり、非アクティブなユニオンメンバーに合法的に左辺値を形成できます(そのため、構築せずに非アクティブなメンバーに割り当てることはできます)、初期化されていないと見なされます。
memcpy
(使用してオブジェクトにアクセスする実装をunsigned char
左辺値)を、それはへのアクセスを禁止*p
した後int *p = 0; const int *const *pp = &p;
(からの暗黙的な変換にもかかわらずint**
にはconst int*const*
有効である)、それもアクセス禁止c
後struct S s; const S &c = s;
。CWG問題616。新しい表現はそれを可能にしますか?[basic.lval]もあります。
&
、ユニオンメンバーに適用した場合の単項演算子の意味も明確にする必要があります(C標準も明確にする必要があります)。結果として得られるポインターは、少なくとも他のメンバーの左辺値の次の直接または間接的な使用まで、メンバーにアクセスするために使用できるはずだと思いますが、gccでは、ポインターはそれ以上使用できないため、何が問題になるのでしょうか。&
オペレータが意味することになっています。
C ++ 11標準はこのように述べています
9.5労働組合
ユニオンでは、最大で1つの非静的データメンバーをいつでもアクティブにできます。つまり、最大で1つの非静的データメンバーの値をいつでもユニオンに格納できます。
1つの値のみが格納されている場合、別の値をどのように読み取ることができますか?それだけではありません。
gccのドキュメントには、これが実装定義の動作にリストされています
- 共用体オブジェクトのメンバーは、異なるタイプのメンバーを使用してアクセスされます(C90 6.3.2.3)。
オブジェクトの表現の関連するバイトは、アクセスに使用されるタイプのオブジェクトとして扱われます。タイプパニングを参照してください。これはトラップ表現である可能性があります。
これは、C標準ではこれが必須ではないことを示しています。
2016-01-05:コメントを通じて、C99欠陥レポート#283にリンクされました。これにより、類似のテキストが脚注としてC標準ドキュメントに追加されます。
78a)ユニオンオブジェクトのコンテンツにアクセスするために使用されるメンバーが、オブジェクトに値を格納するために最後に使用されたメンバーと同じでない場合、値のオブジェクト表現の適切な部分が新しいオブジェクト表現として再解釈されます6.2.6で説明されているタイプ(「タイプパニング」と呼ばれることもあるプロセス)。これはトラップの表現かもしれません。
しかし、脚注が標準の規範ではないことを考えると、それが多くを明確にするかどうかはわかりません。
標準が未定義の動作であると言うのに最も近いのは、共通の初期シーケンスを含む共用体の動作を定義するところです(C99、§6.5.2.3/ 5):
ユニオンの使用を簡略化するために、特別な保証が1つあります。ユニオンに共通の初期シーケンスを共有する複数の構造が含まれている場合(以下を参照)、ユニオンオブジェクトに現在これらの構造の1つが含まれている場合、共通の検査が許可されます。ユニオンの完全な型の宣言が表示される任意の場所のそれらの最初の部分。対応するメンバーが1つ以上の初期メンバーのシーケンスに対して互換性のあるタイプ(ビットフィールドの場合は同じ幅)を持っている場合、2つの構造は共通の初期シーケンスを共有します。
C ++ 11は、§9.2/ 19で同様の要件/許可を提供します。
標準レイアウト共用体に共通の初期シーケンスを共有する2つ以上の標準レイアウト構造体が含まれている場合、および標準レイアウト共用体オブジェクトにこれらの標準レイアウト構造体の1つが現在含まれている場合、任意の共通初期部分を検査できますそのうちの。2つの標準レイアウト構造体は、対応するメンバーにレイアウト互換のタイプがあり、どちらのメンバーもビットフィールドではないか、1つ以上の初期メンバーのシーケンスに対して同じ幅のビットフィールドである場合、共通の初期シーケンスを共有します。
どちらも直接は述べていませんが、これらはどちらもメンバーの「検査」(読み取り)が「許可されている」という強い意味合いを持っています 、1)メンバーが(一部)最近作成されたメンバーであるか、2)共通のイニシャルの一部である場合にのみシーケンス。
それは、それ以外の場合は未定義の動作であるという直接の声明ではありませんが、私が認識している最も近いものです。
union
特定のブログから大丈夫だとの印象を受け、いくつかの大きな構造とプロジェクトを構築したため、sのほとんどのパンニングの使用法が未定義であることを最初に読んだとき、私は素直でした。今、私は考えて私のために、私はすべての後にOKかもしれないunion
sが前面に同じタイプを持つクラス含まれていません
union
が含まれている場合はどうでしょう-この条件がここでも当てはまると思いますが、s のみを許可するように非常に慎重に表現されています。幸いにも、私は生のプリミティブの代わりにそれらをすでに使用していuint8_t
class Something { uint8_t myByte; [...] };
struct
利用可能な回答でまだ言及されていないものは、セクション6.2.5のパラグラフ21の脚注37です。
union型のオブジェクトには一度に1つのメンバーしか含めることができないため、集約型にはunion型が含まれないことに注意してください。
この要件は、メンバーを書き込んだり、別のメンバーを読み取ったりしてはならないことを明確に示しているようです。この場合、仕様の欠如による未定義の動作である可能性があります。
これを例を使ってよく説明します。
次の共用体があると仮定します。
union A{
int x;
short y[2];
};
私はそれsizeof(int)
が4 を与え、それsizeof(short)
が2 を与えると思います。
あなたがunion A a = {10}
それを書くとき、タイプAの新しいvarを作成し、それに値10を入れます。
あなたの記憶は次のようになります:(すべての組合員が同じ場所を取得することを忘れないでください)
| x | | y [0] | y [1] | ----------------------------------------- a-> | 0000 0000 | 0000 0000 | 0000 0000 | 0000 1010 | -----------------------------------------
ご覧のとおり、axの値は10、ay 1の値は10、ay [0]の値は0です。
今、これを行うとどうなりますか?
a.y[0] = 37;
私たちの記憶は次のようになります:
| x | | y [0] | y [1] | ----------------------------------------- a-> | 0000 0000 | 0010 0101 | 0000 0000 | 0000 1010 | -----------------------------------------
これにより、axの値が2424842(10進数)になります。
現在、ユニオンにfloatまたはdoubleが含まれている場合、正確な数値を格納する方法が原因で、メモリマップは混乱します。あなたがここで得ることができるより多くの情報。