1. ホーム
  2. c++

[解決済み】CとC++におけるユニオンの目的

2022-03-30 09:31:12

質問

私は以前からユニオンを快適に使っていましたが、今日、次のような記事を読んで心配になりました。 この記事 というコードがあることを知りました。

union ARGB
{
    uint32_t colour;

    struct componentsTag
    {
        uint8_t b;
        uint8_t g;
        uint8_t r;
        uint8_t a;
    } components;

} pixel;

pixel.colour = 0xff040201;  // ARGB::colour is the active member from now on

// somewhere down the line, without any edit to pixel

if(pixel.components.a)      // accessing the non-active member ARGB::components

つまり、最近書き込んだメンバー以外のメンバーから読み込むと、未定義の動作になるのです。もしこれが意図された組合の使い方でないなら、何が意図されているのでしょうか? どなたか詳しく説明していただけませんか?

更新してください。

後から考えてみると、いくつか明らかにしたいことがありました。

  • 質問の答えはCとC++で同じではありません。無知な若い自分はCとC++の両方とタグ付けしました。
  • C++11の標準を精査した結果、非アクティブなユニオンメンバーへのアクセス/インスペクションが未定義/不特定/実装定義であることを呼び出していると決定的に言うことはできませんでした。私が見つけられたのは§9.5/1だけです。

    標準レイアウトユニオンに共通の初期配列を持つ複数の標準レイアウト構造体が含まれ、この標準レイアウトユニオンタイプのオブジェクトがいずれかの標準レイアウト構造体を含む場合、いずれかの標準レイアウト構造体のメンバの共通の初期配列を検査することが許可されます。§9.2/19: 2つの標準レイアウト構造体は、対応するメンバがレイアウト互換の型を持ち、どちらのメンバもビットフィールドでないか、または両方が1つ以上の初期メンバのシーケンスに対して同じ幅のビットフィールドである場合、共通の初期シーケンスを共有する。

  • C言語では、( C99 TC3 - DR 283 以降)することは合法です( パスカル・キュウクに感謝 をご覧ください。) しかし 未定義の動作につながる可能性があります。 読み込まれた値が、読み込まれた型に対してたまたま無効であった場合(いわゆる "trap representation" )です。そうでなければ、読み込まれた値は実装で定義されています。
  • C89/90ではunspecified behavior (Annex J)で呼び出され、K&Rの本にはimplementation definedと書かれています。K&Rから引用します。

    これはユニオンの目的である、いくつかの型のうちのどれかを合法的に保持できる単一の変数です。[中略)使用法が一貫している限り、取り出される型は最も最近に格納された型でなければなりません。ある型が格納され、別の型が取り出された場合、その結果は実装に依存します。

  • StroustrupのTC++PLより抜粋(強調部分)

    <ブロッククオート

    ユニオンの使用は、データの互換性を保つために不可欠です[...]。 型変換に誤用されることがある となります。

とりわけ、この質問(私が質問したときからタイトルは変わっていません)は、組合の目的を理解する意図で提起されたものであり、規格が許容するものではありません。 例:コードの再利用のために継承を使うことは、もちろんC++の標準では認められていますが C++の言語機能として継承を導入したのは、その目的でも本来の意図でもありません。 . これが、アンドレイの回答が受け入れられ続けている理由です。

解決方法は?

ユニオンの目的は明白なのですが、なぜか人々はそれを見逃すことが多いのです。

ユニオンの目的は メモリを節約する 同じメモリ領域を異なる時間に異なるオブジェクトを格納するために使用することによって。 それなんです。

ホテルの一室のようなものです。さまざまな人が、重ならないように住んでいる。これらの人々は決して会うことはなく、通常、互いのことを何も知りません。部屋のタイムシェアを適切に管理することで、(つまり、異なる人々が同時に一つの部屋に割り当てられないようにすることで、)比較的小さなホテルは比較的多くの人々に宿泊施設を提供することができ、それがホテルの目的である。

それはまさにユニオンの役割です。プログラム中のいくつかのオブジェクトが、重複しない値の寿命を持つ値を保持していることが分かっている場合、これらのオブジェクトをユニオンにマージすることで、メモリを節約することができます。ホテルの部屋は、各時点で最大1つのテナントを持つように、組合はプログラム時間の各時点で最大1つのメンバーを持っています。読み取ることができるのは、"active"メンバーだけです。他のメンバーに書き込むと、そのメンバーに"active"のステータスが切り替わります。

ある理由で、このユニオンの本来の目的は、ユニオンのあるメンバーを書き、別のメンバーを通してそれを検査するという、まったく別のものにオーバーライドされてしまったのです。このようなメモリの再解釈(別名:type punning")というのは <ストライク は、ユニオンの有効な使い方ではありません。一般に、未定義の動作につながります は、C89/90では実装定義された動作を生み出すと説明されています。

EDITです。 型パニング(あるメンバを書き、別のメンバを読む)のためのユニオンの使用は、C99標準の技術的な正誤表の1つでより詳細な定義が与えられています(下記参照)。 DR#257 DR#283 ). しかし、形式的には、トラップ表現を読み込もうとすることによって未定義の動作に陥ることを防ぐことはできないことに留意してください。