ストラクチャーとユニオンの違い

ユニオンでは、すべての要素が同じ場所に格納されているため、要素の 1 つだけを使用することになっています。これは、いくつかのタイプのいずれかになる可能性があるものを保存する場合に役立ちます。一方、構造体は、その要素ごとに個別のメモリ位置を持ち、それらすべてを一度に使用できます。

それらの使用の具体的な例を挙げると、私は少し前にSchemeインタープリターに取り組んでいて、基本的にSchemeデータ型をCデータ型にオーバーレイしていました。これには、値の型を示す列挙型とその値を格納する共用体を構造体に格納することが含まれていました。

union foo {
  int a;   // can't use both a and b at once
  char b;
} foo;

struct bar {
  int a;   // can use both a and b simultaneously
  char b;
} bar;

union foo x;
x.a = 3; // OK
x.b = 'c'; // NO! this affects the value of x.a!

struct bar y;
y.a = 3; // OK
y.b = 'c'; // OK

編集: x.b を 'c' に設定すると x.a の値がどのように変化するか疑問に思っている場合は、技術的に言えば未定義です。最近のほとんどのマシンでは、char は 1 バイト、int は 4 バイトであるため、x.b に値「c」を指定すると、x.a の最初のバイトにも同じ値が与えられます:

union foo x;
x.a = 3;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);

版画

99, 99

2つの値が同じなのはなぜですか? int 3 の最後の 3 バイトはすべて 0 であるため、99 としても読み取られます。x.a に大きな数値を入力すると、常にそうであるとは限らないことがわかります:

union foo x;
x.a = 387439;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);

版画

387427, 99

実際のメモリ値を詳しく調べるために、値を 16 進数で設定して出力してみましょう:

union foo x;
x.a = 0xDEADBEEF;
x.b = 0x22;
printf("%x, %x\n", x.a, x.b);

版画

deadbe22, 22

0x22 が 0xEF を上書きした場所がはっきりとわかります。

でも

C では、int 内のバイトの順序は定義されていません。 このプログラムは、私の Mac では 0xEF を 0x22 で上書きしましたが、int を構成するバイトの順序が逆になっているため、代わりに 0xDE を上書きする他のプラットフォームがあります。したがって、プログラムを作成するときは、ユニオン内の特定のデータを上書きする動作に依存しないでください。移植性がないためです。

バイト順の詳細については、エンディアンを確認してください。


簡単な答えは次のとおりです。構造体はレコード構造体です。構造体の各要素は新しいスペースを割り当てます。したがって、次のような構造体

struct foobarbazquux_t {
    int foo;
    long bar;
    double baz; 
    long double quux;
}

少なくとも (sizeof(int)+sizeof(long)+sizeof(double)+sizeof(long double)) を割り当てます 各インスタンスのメモリ内のバイト。 (「少なくとも」は、アーキテクチャのアラインメントの制約により、コンパイラが構造体にパディングを強制する可能性があるためです。)

一方、

union foobarbazquux_u {
    int foo;
    long bar;
    double baz; 
    long double quux;
}

メモリのチャンクを 1 つ割り当て、それに 4 つのエイリアスを割り当てます。だから sizeof(union foobarbazquux_u) ≥ max((sizeof(int),sizeof(long),sizeof(double),sizeof(long double)) 、再びアラインメントのための追加の可能性があります.


架空の通信プロトコル

struct packetheader {
   int sourceaddress;
   int destaddress;
   int messagetype;
   union request {
       char fourcc[4];
       int requestnumber;
   };
};

この架空のプロトコルでは、「メッセージ タイプ」に基づいて、ヘッダー内の次の場所が要求番号または 4 文字のコードのいずれかであるが、両方ではないことが特定されています。つまり、共用体を使用すると、同じ格納場所で複数のデータ型を表すことができ、一度に 1 つの型のデータのみを格納することが保証されます。

共用体は主に、システム プログラミング言語としての C の遺産に基づく低レベルの詳細であり、「重複する」記憶域の場所がこのように使用されることがあります。一度に複数の型のうちの 1 つだけが保存されるデータ構造がある場合、共用体を使用してメモリを保存することができます。

一般に、OS は構造体と共用体を気にしたり認識したりしません。これらはどちらも単にメモリのブロックにすぎません。構造体は、複数のデータ オブジェクトを格納するメモリ ブロックであり、それらのオブジェクトは重複しません。ユニオンは、複数のデータ オブジェクトを格納するメモリのブロックですが、最大のデータ オブジェクトしか格納できないため、一度に 1 つのデータ オブジェクトしか格納できません。