C/C++中的联合(union)大小是多少?它是其中最大数据类型的大小吗?如果是,那么如果联合中较小的数据类型处于激活状态,编译器如何计算移动堆栈指针的方式?
union
总是占用与最大成员相同的空间。无论当前使用的是什么。
union {
short x;
int y;
long long z;
}
上述的 union
的实例始终需要至少使用一个 long long
来进行存储。
附注:正如Stefano所指出的那样,任何类型(union
, struct
, class
)实际占用的空间确实取决于编译器执行对齐等其他问题。出于简单起见,我没有详细说明这些问题,只是想告诉大家 union 考虑了最大的项目大小。 重要的是要知道,实际大小确实取决于对齐方式。
联合体中最多只能有一个数据成员处于活动状态,也就是说,在联合体中最多只能存储一个数据成员的值。[注意:为了简化使用union而作出一项特殊保证:如果POD-union包含共享公共初始序列(9.2)的多个POD-struct,并且如果这种POD-union类型的对象包含POD-struct之一,则允许检查任何POD-struct成员的公共初始序列;请参见9.2。]联合体的大小足以容纳其数据成员中最大的那个。每个数据成员都被分配一个像它是一个结构体的唯一成员的方式。
这意味着每个成员共享同一内存区域。至多有一个成员处于活动状态,但你不能确定哪一个。你需要在其他地方自己存储关于当前活动成员的信息。将这样的标志与联合体一起存储(例如使用一个带有整数类型标志和一个联合体数据存储的结构体)将给你一个所谓的“带判别联合”:一个知道当前“活动类型”的联合。
一种常见的用法是在词法分析器中,可以有不同的标记,但根据标记,需要存储不同的信息(将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
,因为这是每个成员的公共初始序列。
存在编译器扩展,允许访问所有成员,无论哪一个当前存储了它的值。这允许在每个成员之间以不同类型重新解释存储的位,从而实现高效的重组。例如,以下内容可用于将浮点变量分解为2个无符号短整型:
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您需要使用pragma指令。
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,编译器在栈上分配0xd而不是0x10。
联合体中没有所谓的活动数据类型。您可以自由读写联合体的任何“成员”:对于您来说,这是解释您获得的内容的问题。
因此,联合体的sizeof始终是其最大数据类型的sizeof。
g ++
中),则是实现定义的规则。 - underscore_d这个大小至少是最大的组合类型的大小。没有“活动”类型的概念。
你应该把union看作一个容器,其中包含最大的数据类型,并结合强制转换的快捷方式。当你使用较小的成员之一时,未使用的空间仍然存在,但它只是保持未使用状态。
在Unix下,你经常会看到它与ioctl()调用结合使用,所有ioctl()调用都将传递相同的结构体,其中包含所有可能的响应的union。例如,这个例子来自于/usr/include/linux/if.h,这个结构体被用于ioctl()的以太网接口的配置/查询状态,请求参数定义了哪个union部分实际上正在使用:
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;
};
在C/C++中,union的大小是多少?它是其内部最大数据类型的大小吗?
是的,union的大小是其最大成员的大小。
例如:
#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
。最大成员的大小。
这就是为什么联合体通常在一个结构体内部有一个标志,指示哪个是“活动”成员。
例子:
struct ONE_OF_MANY {
enum FLAG { FLAG_SHORT, FLAG_INT, FLAG_LONG_LONG } flag;
union { short x; int y; long long z; };
};