C/C++ 中联合的大小是多少?它是里面最大数据类型的大小吗?如果是这样,如果联合的较小数据类型之一处于活动状态,编译器如何计算如何移动堆栈指针?
8 回答
Aunion
总是占用与最大成员一样多的空间。当前使用什么并不重要。
union {
short x;
int y;
long long z;
}
上面的一个实例union
总是至少需要一个long long
用于存储。
旁注:正如Stefano所指出的,任何类型 ( union
, struct
, class
) 将占用的实际空间确实取决于其他问题,例如编译器的对齐。为简单起见,我没有进行此操作,因为我只是想告诉工会考虑了最大的项目。重要的是要知道实际大小确实取决于对齐方式。
该标准回答了 C++ 标准第 9.5 节或 C99 标准第 6.5.2.3 节第 5 段(或 C11 标准第 6 段,或 C18 标准第 6.7.2.1 节第 16 段)中的所有问题:
在一个联合中,任何时候最多有一个数据成员处于活动状态,即任何时候最多可以有一个数据成员的值存储在一个联合中。[注意:为了简化联合的使用,做了一个特殊的保证:如果一个 POD 联合包含多个共享一个公共初始序列的 POD 结构(9.2),并且如果这个 POD 联合类型的对象包含以下之一POD-structs,允许检查任何 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
联合没有活动数据类型的概念。你可以自由地阅读和写作工会的任何“成员”:这取决于你来解释你得到什么。
因此,联合的大小始终是其最大数据类型的大小。
大小将至少是最大的组成类型的大小。没有“活动”类型的概念。
您真的应该将联合视为其中最大数据类型的容器,并结合转换的快捷方式。当您使用其中一个较小的成员时,未使用的空间仍然存在,但它只是保持未使用状态。
在 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;
};
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
.
如果联合的较小数据类型之一处于活动状态,编译器如何计算如何移动堆栈指针?
它由编译器在内部处理。假设我们正在访问联合的数据成员之一,那么我们无法访问其他数据成员,因为我们可以访问联合的单个数据成员,因为每个数据成员共享相同的内存。通过使用联合,我们可以节省大量宝贵的空间。
最大成员的大小。
这就是为什么联合通常在具有指示哪个是“活动”成员的标志的结构中有意义。
例子:
struct ONE_OF_MANY {
enum FLAG { FLAG_SHORT, FLAG_INT, FLAG_LONG_LONG } flag;
union { short x; int y; long long z; };
};