C / C ++中联合的大小是多少?它是最大数据类型的sizeof吗?如果是这样,如果联合的较小数据类型之一处于活动状态,编译器如何计算如何移动堆栈指针?
答案 0 :(得分:56)
union
总是占用最大成员的空间。目前使用的无关紧要。
union {
short x;
int y;
long long z;
}
以上union
的实例始终至少需要long long
才能存储。
旁注:正如Stefano所述,任何类型的实际空间(union
,struct
,class
)都将取决于在其他问题上,例如编译器的对齐。我没有简单地通过这个,因为我只想告诉工会考虑最大的项目。 了解实际尺寸 取决于对齐非常重要。
答案 1 :(得分:28)
标准回答了C ++标准第9.5节或C99标准第6.5.2.3节(或C11标准第6段)中的所有问题:
在联合中,最多一个数据成员可以随时处于活动状态,也就是说,任何时候最多一个数据成员的值都可以存储在一个联合中。 [注意:为了简化联合的使用,我们做了一个特别的保证:如果一个POD-union包含几个共享一个公共初始序列的POD结构(9.2),并且这个POD-union类型的一个对象包含一个在POD结构中,允许检查任何POD结构成员的共同初始序列;见9.2。 ] union的大小足以包含其最大的数据成员。每个数据成员都被分配,就好像它是结构的唯一成员一样。
这意味着每个成员共享相同的内存区域。 最多只有一个成员有效,但你找不到哪一个。您必须在其他地方存储有关当前活动成员的信息。除了union之外还存储这样的标志(例如,有一个结构,其中一个整数作为type-flag,union作为数据存储)将给你一个所谓的“区别联合”:一个知道什么类型的联合它目前是“活跃的”。
一个常见的用法是在词法分析器中,你可以使用不同的标记,但根据标记,你有不同的信息要存储(将line
放入每个结构中以显示常见的初始序列): / p>
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; };
编写低级代码时,这非常方便。如果编译器不支持该扩展,但无论如何都要支持,那么编写未定义结果的代码。因此,如果您使用该技巧,请确保您的编译器支持它。
答案 2 :(得分:16)
这取决于编译器和选项。
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
答案 3 :(得分:10)
联合没有活动数据类型的概念。你可以自由地阅读和写作工会的任何“成员”:这取决于你解释你得到的东西。
因此,union的大小始终是其最大数据类型的大小。
答案 4 :(得分:3)
大小至少是最大作曲类型的大小。没有“主动”类型的概念。
答案 5 :(得分:2)
您应该将union作为其中最大数据类型的容器以及转换的快捷方式。当您使用其中一个较小的成员时,未使用的空间仍然存在,但它只是保持未使用状态。
您经常会在Unix中看到这与ioctl()调用结合使用,所有ioctl()调用都将传递相同的结构,其中包含所有可能响应的并集。例如。 这个例子来自/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;
};
答案 6 :(得分:0)
最大会员的人数。
这就是为什么联盟通常在一个结构中有意义,该结构有一个标志,指示哪个是“活跃”成员。
示例:
struct ONE_OF_MANY {
enum FLAG { FLAG_SHORT, FLAG_INT, FLAG_LONG_LONG } flag;
union { short x; int y; long long z; };
};
答案 7 :(得分:0)
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
。
编译器如何计算如何移动堆栈指针(如果有) union的较小数据类型是否处于活动状态?
它由编译器在内部处理。假设我们正在访问union的一个数据成员,那么我们无法访问其他数据成员,因为我们可以访问union的单个数据成员,因为每个数据成员共享相同的内存。通过使用联盟,我们可以节省大量有价值的空间。