C/C++ の共用体のサイズは? その中の最大のデータ型のサイズですか?もしそうなら、共用体のより小さいデータ型の 1 つがアクティブな場合、コンパイラはどのようにスタック ポインタを移動するかを計算しますか?
8 に答える
Aunion
は、常に最大のメンバーと同じスペースを占有します。現在使用中のものは問いません。
union {
short x;
int y;
long long z;
}
上記のインスタンスは、ストレージunion
に少なくとも a を常に必要とします。long long
サイドノート: Stefanounion
が指摘したように、型 ( 、struct
、 ) が取る実際のスペースはclass
、コンパイラによるアラインメントなどの他の問題に依存します。ユニオンが最大のアイテムを考慮に入れることを伝えたかっただけなので、簡単にするためにこれを実行しませんでした。実際のサイズは配置に依存することを知っておくことが重要です。
この標準は、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; };
これは、低レベルのコードを書くときに非常に便利です。コンパイラがその拡張機能をサポートしていないにもかかわらず、それを行う場合、結果が定義されていないコードを記述します。そのため、そのトリックを使用する場合は、コンパイラがそれをサポートしていることを確認してください。
コンパイラとオプションに依存します。
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 をスタックに割り当てます。
共用体にはアクティブなデータ型の概念はありません。組合の「メンバー」を自由に読み書きできます。これは、得られるものを解釈するのはあなた次第です。
したがって、共用体のサイズは常にその最大のデータ型のサイズになります。
サイズは、少なくとも最大の合成タイプのサイズになります。「アクティブ」タイプの概念はありません。
ユニオンは、キャストのショートカットと組み合わせて、その中の最大のデータ型のコンテナーとして実際に見る必要があります。小さいメンバーの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;
};
最大のメンバーのサイズ。
これが、どのメンバーが「アクティブな」メンバーであるかを示すフラグを持つ構造体の内部で共用体が通常意味をなす理由です。
例:
struct ONE_OF_MANY {
enum FLAG { FLAG_SHORT, FLAG_INT, FLAG_LONG_LONG } flag;
union { short x; int y; long long z; };
};