web-dev-qa-db-ja.com

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

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

40
Naveen

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

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

つまり、各メンバーは同じメモリ領域を共有します。そこにis最大で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つの符号なしshortに分割できます。

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

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

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

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

上記のunionのインスタンスは、少なくともlong longストレージ用。

サイドノートStefano で述べたように、実際のスペースはあらゆるタイプ(unionstructclass)は、コンパイラーによるアライメントなどの他の問題に依存します。組合が最大の品目を考慮に入れていることを伝えたかったので、簡単にするためにこれを通りませんでした。 実際のサイズはdoesがalignmentに依存することを知ることが重要です。

58
Mehrdad Afshari

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

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に割り当てます

17
Stefano Borini

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

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

11
mouviciel

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

3
anon

ユニオンは、キャスト内の最大のデータ型のコンテナーとキャストのショートカットを組み合わせたものとして実際に見る必要があります。小さいメンバーの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;
};
2
amo-ej1
  1. 最大メンバーのサイズ。

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

例:

struct ONE_OF_MANY {
    enum FLAG { FLAG_SHORT, FLAG_INT, FLAG_LONG_LONG } flag;
    union { short x; int y; long long z; };
};
0
pyon

C/C++のユニオンのサイズは?内部の最大データ型のサイズですか?

はい、ユニオンのサイズは最大メンバーのサイズです。

例えば ​​:

#include<stdio.h>

union un
{
    char c;
    int i;
    float f;
    double d;
};

int main()
{
    union un u1;
    printf("sizeof union u1 : %ld\n",sizeof(u1));
    return 0;
}

出力:

sizeof union u1 : 8
sizeof double d : 8

ここで最大のメンバーはdoubleです。両方ともサイズ8。したがって、sizeofが正しく言ったように、ユニオンのサイズは実際に8

ユニオンの小さいデータ型の1つがアクティブな場合、コンパイラはどのようにスタックポインタを移動するかを計算しますか?

コンパイラによって内部的に処理されます。ユニオンのデータメンバの1つにアクセスすると、各データメンバは同じメモリを共有するため、ユニオンの単一のデータメンバにアクセスできるため、他のデータメンバにはアクセスできません。 Unionを使用することにより、貴重なスペースを大幅に節約できます。

0
msc