1.概述
联合体union的定义方式与结构体一样,但是二者有根本区别。
在结构中各成员有各自的内存空间,一个结构变量的总长度是各成员长度之和。而在“联合”中,各成员共享一段内存空间,一个联合变量的长度等于各成员中最长的长度。
2.联合体长度
在The C Programming Language里面讲述union内存分配的原话是
1)联合体就是一个结构
2)联合体的所有成员相对于基地址的偏移量为0
3)此结构空间要大到总够容纳最“宽”的成员
4)并且,其对齐方式要适合于联合体中所有类型的成员
我的理解可以概括为两点:
1)联合体的结构空间要足够大,要等于最长的一个结构变量的空间,但是这个最长的空间要满足以下条件:
1.要大于等于最长的一个结构变量的空间
2.并且要能够整除其他结构变量的数据长度,即联合体空间对其他成员的元类型要能够整除(int a[5],其元类型为int,元类型长度为4),实际上就是要取一个元类型的最小公倍数。
这儿举例来说
union { float fuel_load; char a[5]; int pallets; }fighter;
这个结构体中,各个结构变量的空间分别为float fuel_load; 占4个字节,char a[5];占5个字节,int pallets;占4个字节。通过“3)此结构空间要大到总够容纳最“宽”的成员”这句话,我们可以认为是结构体的空间为5个字节即可,但是“其对齐方式要适合于联合体中所有类型的成员”没有满足,对于这个问题,通过上面红色字体部分可以解决。,因此联合体空间为8.8可以整除 4(float、int长度)和1(char的长度),并且8大于数组5.
再举一个例子有助于大家理解。
struct aircraft { int wingspan; int passengers; union { float fuel_load; float bomb_load; int pallets; }; }fighter;
sizeof(fighter) 是12 。int wingspan; int passengers;两个int型 8个字节。union中 3个都是4个字节,因此union长度为4个字节。
3.内存分配
一句话:联合体变量的各个成员都是从低字节开始公用的。即:所有的成员都是从低字节开始的。
我们先为整个union分配一个空间,这个空间大小就是上面(2)内存分配中所讲述的。
union { int i; char x[2]; }a; int main(void) { a.x[0] = 10; a.x[1] = 1; printf("%d\n",a.i); return 0; }
其内存如下图所示。a.x[0] 处于低字节,x[1]高字节。当调用i这个成员变量的时候,其开始地址仍然是从起始地址开始,数4个字节输出。因此为 256 + 10 = 266
(改低地址处:0000 1010)
程序附上:
#include <string.h> #include <stdio.h> #include <unistd.h> #if 0 typedef union data{ float a; float b; int c; }data_t; int main(int argc ,char **argv) { printf("sizeof(float):%d\nsizeof(data_t):%d\n",sizeof(float),sizeof(data_t)); return 0; } #endif typedef union data{ int i; char x[2]; }data_t; int main(int argc ,char **argv) { data_t datab; memset(&datab,0,sizeof(datab)); datab.x[0] = 10; datab.x[1] = 1; printf("sizeof(float):%d\nsizeof(int):%d\n",sizeof(float),sizeof(int)); printf("datab.i :%d\n",datab.i); return 0; }
4.附录 各个数据类型的长度
type bytes
int 4
char 1
short int 2
bool 1
long 4
long long 8
float 4
double 8
long double 8
补充1:
解决一下捧场网友的困惑。
关于“有名”与“无名”联合体在结构体内所占空间的问题,其实这和是不是结构体无关,只和“有名”、“无名”有关,而且有名无名也是表象,其实是声明类型与定义变量的区别,看例子,直接打印,
#include <stdio.h> struct s1{ union u{ int i; }; struct ss1{ int i; }; }; struct s2{ union{ int i; }; struct{ int i; }; }; struct s3{//the same to s2 union su3{ int i; }su33; struct ss3{ int i; }ss33; }; union su4{ int i; }; struct ss4{ int i; }; struct s4{//the same to s3 union su4 su44; struct ss4 ss44; }; struct s5{//the same to s1 union su4; struct ss4; }; struct s6{//the same to s1 union{ int; }; struct{ int; }; }; main(){ struct s1 sVal1; struct s2 sVal2; struct s3 sVal3; struct s4 sVal4; struct s5 sVal5; struct s6 sVal6; printf("sVal1's size:%d\n",sizeof(sVal1)); printf("sVal1:%p\t%d\n",&sVal1); printf("sVal2's size:%d\n",sizeof(sVal2)); printf("sVal2:%p\t%d\n",&sVal2); printf("sVal3's size:%d\n",sizeof(sVal3)); printf("sVal3:%p\t%d\n",&sVal3); printf("sVal4's size:%d\n",sizeof(sVal4)); printf("sVal4:%p\t%d\n",&sVal4); printf("sVal5's size:%d\n",sizeof(sVal5)); printf("sVal5:%p\t%d\n",&sVal5); printf("sVal5's size:%d\n",sizeof(sVal5)); printf("sVal5:%p\t%d\n",&sVal5); }
地址供参考,主要看size,分别为:
0,8,8,8,0,0
s1只有类型,没有变量,没有变量自然就没有空间占用(s5同)。
类型就是类型,和是不是结构体、联合体无关的,你的“int i;”中i不就是个变量吗?如果换成int;结果相同(这就是s6)。
s4和s5的做法能帮助排除干扰,将子结构体与联合体声明在外,内部直接引用,4是定义了变量,5什么都没做。
另外,这种做法编译的时候GCC会给你在相应的行做出提示“union_with_name.c:49: 警告:没有声明任何东西”