C / C ++中的一个联合的大小

C / C ++中union的大小是多less? 它里面最大的数据types的大小? 如果是这样,如果联合的一个较小的数据types是活动的,编译器如何计算如何移动堆栈指针?

本标准回答了C ++标准的第9.5节或C99标准(或C11标准第6段)第6.5.2.3节的所有问题:

在一个工会中,最多只有一个数据成员可以随时处于活动状态,也就是说,最多只有一个数据成员的价值可以随时存储在一个工会中。 [注意:为了简化联合的使用,做了一个特殊的保证:如果一个POD-union包含多个共享一个初始序列(9.2)的POD-structs,并且这个POD-uniontypes的一个对象包含POD结构,允许检查任何POD结构成员的共同初始序列; 见9.2。 ]联合的大小足以包含最大的数据成员。 每个数据成员都被分配好像它是结构的唯一成员。

这意味着每个成员共享相同的内存区域。 最多只有一个成员活跃,但是你找不到哪一个成员。 您将不得不在其他地方存储有关当前活动成员的信息。 除联合之外存储这样一个标志(例如,将一个带有一个整数的结构作为types标志和一个联合作为数据存储)将会给你一个所谓的“区别联合”:一个知道什么types的联合它目前是“积极的”。

一个常见的用法是在词法分析器中,您可以使用不同的标记,但取决于标记,您需要存储不同的信息(将每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; }; 

该标准允许您访问每个成员的行,因为这是每个成员的共同的初始序列。

有一些编译器扩展允许访问所有成员,而不pipe当前哪个成员存储了它的值。 这允许在每个成员之间有效地重新解释具有不同types的存储位。 例如,下面的代码可以用来parsing一个floatvariables为2个无符号短裤:

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

编写低级代码时,这可能会非常方便。 如果编译器不支持该扩展,但无论如何你都要这么做,那么你就编写了没有定义结果的代码。 所以如果你使用这个技巧,请确定你的编译器支持它。

一个union总是占用最大的成员空间。 目前使用什么并不重要。

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

上述union一个例子总是需要至lesslong long才能存储。

注意 :正如Stefano所指出的,任何types( unionstructclass )的实际空间将取决于其他问题,例如编译器的alignment。 我只是想告诉工会考虑最大的项目,我并没有简单说明这一点。 知道实际大小取决于alignment是很重要的

这取决于编译器和选项。

 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

如果我没有记错,它取决于编译器放入分配空间的alignment方式。 所以,除非你使用了一些特殊的选项,编译器会把填充放到你的联合空间中。

编辑:用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 

其中唯一的区别是在主+ 13,编译器分配堆栈0xd而不是0x10

联合体没有活动数据types的概念。 你可以自由阅读和编写工会的任何“成员”:这是由你来解释你得到的。

因此,联合的大小始终是其最大数据types的大小。

大小至less是最大的组合types的大小。 没有“主动”types的概念。

你应该真的把联合看做是一个容器,把它里面最大的数据types和一个转换的快捷键结合起来。 当你使用其中一个较小的成员时,未使用的空间仍然存在,但是它仍然没有被使用。

你经常会看到这个和Unix下的ioctl()调用结合使用,所有的ioctl()调用都会传递相同的结构,它包含所有可能的响应的联合。 例如,这个例子来自/usr/include/linux/if.h,这个结构被用于ioctl()的configuration/查询以太网接口的状态,请求参数定义了union的哪一部分实际上正在使用:

 struct ifreq { #define IFHWADDRLEN 6 union { char ifrn_name[IFNAMSIZ]; /* if name, eg "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; }; 
  1. 最大的成员的大小。

  2. 这就是为什么工会通常在有一个标志表明哪一个是“活动”成员的结构中有意义。

例:

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

C / C ++中union的大小是多less? 它是它里面最大的数据types的大小吗?

是的 ,工会的规模是其最大成员的规模。

例如 :

 #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

如果联合的一个较小的数据types是活动的,编译器如何计算如何移动堆栈指针?

它由编译器内部处理。 假设我们正在访问联合的一个数据成员,那么我们就不能访问其他数据成员,因为我们可以访问联合的单个数据成员,因为每个数据成员共享相同的内存。 通过使用联盟,我们可以节省很多宝贵的空间。