C/C++ の共用体のサイズ

union 常に最大のメンバーと同じスペースを占有します。現在何が使用されているかは問題ではありません。

union {
  short x;
  int y;
  long long z;
}

上記の union のインスタンス 常に少なくとも long long かかります

補足 :ステファノが指摘したように、実際のスペースは任意のタイプ(unionstructclass ) は、コンパイラによるアラインメントなどの他の問題に依存します。ユニオンが最大のアイテムを考慮に入れることを伝えたかっただけなので、簡単にするためにこれを実行しませんでした。 実際のサイズは あることを知っておくことが重要です アライメントに依存 .


この標準は、C++ 標準のセクション 9.5、または C99 標準のセクション 6.5.2.3 パラグラフ 5 (または C11 標準のパラグラフ 6、または C18 標準のセクション 6.7.2.1 パラグラフ 16) のすべての質問に答えます。

つまり、各メンバーは同じメモリ領域を共有します。 ある アクティブなメンバーは最大で 1 人ですが、どのメンバーかはわかりません。現在アクティブなメンバーに関する情報を別の場所に保存する必要があります。そのようなフラグを共用体に加えて格納すると (たとえば、型フラグとして整数を持ち、データ ストアとして共用体を持つ構造体を持つ)、いわゆる「判別共用体」が得られます。それは現在「アクティブなもの」です。

一般的な用途の 1 つはレクサーで、さまざまなトークンを使用できますが、トークンに応じて、格納する情報が異なります (line を配置します)。 一般的な初期シーケンスが何であるかを示すために、各構造体に挿入します):

struct tokeni {
    int token; /* type tag */
    union {
        struct { int line; } noVal;
        struct { int line; int val; } intVal;
        struct { int line; struct string val; } stringVal;
    } data;
};

標準では line にアクセスできます これは、各メンバーの共通の初期シーケンスであるためです。

現在値が保存されているメンバーに関係なく、すべてのメンバーにアクセスできるコンパイラ拡張機能が存在します。これにより、各メンバー間で異なるタイプの格納されたビットを効率的に再解釈できます。たとえば、以下は float 変数を 2 つの unsigned short に分割するために使用できます:

union float_cast { unsigned short s[2]; float f; };

これは、低レベルのコードを書くときに非常に便利です。コンパイラがその拡張機能をサポートしていないにもかかわらず、それを行う場合、結果が定義されていないコードを記述します。そのため、そのトリックを使用する場合は、コンパイラがそれをサポートしていることを確認してください。


コンパイラとオプションに依存します。

int main() {
  union {
    char all[13];
    int foo;
  } record;

printf("%d\n",sizeof(record.all));
printf("%d\n",sizeof(record.foo));
printf("%d\n",sizeof(record));

}

この出力:

13416

私の記憶が正しければ、コンパイラが割り当てられたスペースに配置するアラインメントに依存します。そのため、特別なオプションを使用しない限り、コンパイラはユニオン スペースにパディングを挿入します。

編集:gcc では、プラグマ ディレクティブを使用する必要があります

int main() {
#pragma pack(push, 1)
      union {
           char all[13];
           int foo;
      } record;
#pragma pack(pop)

      printf("%d\n",sizeof(record.all));
      printf("%d\n",sizeof(record.foo));
      printf("%d\n",sizeof(record));

}

これは出力します

13413

逆アセンブルからも確認できます (わかりやすくするために一部の printf を削除しています)

  0x00001fd2 <main+0>:    push   %ebp             |  0x00001fd2 <main+0>:    push   %ebp
  0x00001fd3 <main+1>:    mov    %esp,%ebp        |  0x00001fd3 <main+1>:    mov    %esp,%ebp
  0x00001fd5 <main+3>:    push   %ebx             |  0x00001fd5 <main+3>:    push   %ebx
  0x00001fd6 <main+4>:    sub    $0x24,%esp       |  0x00001fd6 <main+4>:    sub    $0x24,%esp
  0x00001fd9 <main+7>:    call   0x1fde <main+12> |  0x00001fd9 <main+7>:    call   0x1fde <main+12>
  0x00001fde <main+12>:   pop    %ebx             |  0x00001fde <main+12>:   pop    %ebx
  0x00001fdf <main+13>:   movl   $0xd,0x4(%esp)   |  0x00001fdf <main+13>:   movl   $0x10,0x4(%esp)                                         
  0x00001fe7 <main+21>:   lea    0x1d(%ebx),%eax  |  0x00001fe7 <main+21>:   lea    0x1d(%ebx),%eax
  0x00001fed <main+27>:   mov    %eax,(%esp)      |  0x00001fed <main+27>:   mov    %eax,(%esp)
  0x00001ff0 <main+30>:   call  0x3005 <printf>   |  0x00001ff0 <main+30>:   call   0x3005 <printf>
  0x00001ff5 <main+35>:   add    $0x24,%esp       |  0x00001ff5 <main+35>:   add    $0x24,%esp
  0x00001ff8 <main+38>:   pop    %ebx             |  0x00001ff8 <main+38>:   pop    %ebx
  0x00001ff9 <main+39>:   leave                   |  0x00001ff9 <main+39>:   leave
  0x00001ffa <main+40>:   ret                     |  0x00001ffa <main+40>:   ret    

唯一の違いは main+13 で、コンパイラは 0x10 ではなく 0xd をスタックに割り当てます