52

C/C++ の共用体のサイズは? その中の最大のデータ型のサイズですか?もしそうなら、共用体のより小さいデータ型の 1 つがアクティブな場合、コンパイラはどのようにスタック ポインタを移動するかを計算しますか?

4

8 に答える 8

68

Aunionは、常に最大のメンバーと同じスペースを占有します。現在使用中のものは問いません。

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

上記のインスタンスは、ストレージunionに少なくとも a を常に必要とします。long long

サイドノート: Stefanounionが指摘したように、型 ( 、struct、 ) が取る実際のスペースはclass、コンパイラによるアラインメントなどの他の問題に依存します。ユニオンが最大のアイテムを考慮に入れることを伝えたかっただけなので、簡単にするためにこれを実行しませんでした。実際のサイズ配置に依存することを知っておくことが重要です。

于 2009-04-11T18:28:21.583 に答える
36

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

共用体では、いつでもアクティブにできるデータ メンバーは最大で 1 つです。つまり、データ メンバーの最大で 1 つの値をいつでも共用体に格納できます。[注: 共用体の使用を簡素化するために、1 つの特別な保証が行われます: POD 共用体に、共通の初期シーケンス (9.2) を共有する複数の POD 構造体が含まれている場合、およびこの POD 共用体タイプのオブジェクトに次のいずれかが含まれている場合POD 構造体の場合、任意の POD 構造体メンバーの共通の初期シーケンスを検査することが許可されています。9.2 を参照してください。] 共用体のサイズは、最大のデータ メンバーを格納するのに十分です。各データ メンバーは、構造体の唯一のメンバーであるかのように割り当てられます。

つまり、各メンバーは同じメモリ領域を共有します。アクティブなメンバー最大で 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; };

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

于 2009-04-11T19:44:12.663 に答える
21

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

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));

}

これは以下を出力します:

13 4 16

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

編集: 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));

}

これは出力します

13 4 13

逆アセンブルからも見ることができます(明確にするためにいくつかの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 をスタックに割り当てます。

于 2009-04-11T19:20:01.130 に答える
12

共用体にはアクティブなデータ型の概念はありません。組合の「メンバー」を自由に読み書きできます。これは、得られるものを解釈するのはあなた次第です。

したがって、共用体のサイズは常にその最大のデータ型のサイズになります。

于 2009-04-11T18:33:30.120 に答える
3

サイズは、少なくとも最大の合成タイプのサイズになります。「アクティブ」タイプの概念はありません。

于 2009-04-11T18:33:40.430 に答える
2

ユニオンは、キャストのショートカットと組み合わせて、その中の最大のデータ型のコンテナーとして実際に見る必要があります。小さいメンバーの1つを使用すると、未使用のスペースは残りますが、単に未使用のままになります。

これは、Unixでioctl()呼び出しと組み合わせて使用​​されることがよくあります。すべてのioctl()呼び出しは、すべての可能な応答の和集合を含む同じ構造体を渡します。たとえば、この例は/usr/include/linux/if.hからのものであり、この構造体はioctl()でイーサネットインターフェイスの状態を構成/クエリするために使用されます。要求パラメーターは、ユニオンのどの部分が実際に使用されているかを定義します。 :

struct ifreq 
{
#define IFHWADDRLEN 6
    union
    {
        char    ifrn_name[IFNAMSIZ];        /* if name, e.g. "en0" */
    } ifr_ifrn;

    union {
        struct  sockaddr ifru_addr;
        struct  sockaddr ifru_dstaddr;
        struct  sockaddr ifru_broadaddr;
        struct  sockaddr ifru_netmask;
        struct  sockaddr ifru_hwaddr;
        short   ifru_flags;
        int ifru_ivalue;
        int ifru_mtu;
        struct  ifmap ifru_map;
        char    ifru_slave[IFNAMSIZ];   /* Just fits the size */
        char    ifru_newname[IFNAMSIZ];
        void *  ifru_data;
        struct  if_settings ifru_settings;
    } ifr_ifru;
};
于 2009-04-11T18:49:58.987 に答える
0
  1. 最大のメンバーのサイズ。

  2. これが、どのメンバーが「アクティブな」メンバーであるかを示すフラグを持つ構造体の内部で共用体が通常意味をなす理由です。

例:

struct ONE_OF_MANY {
    enum FLAG { FLAG_SHORT, FLAG_INT, FLAG_LONG_LONG } flag;
    union { short x; int y; long long z; };
};
于 2009-04-11T19:16:00.377 に答える