10.3 共用体、枚举类型与typedef
10.3.1 共用体
共用体,也叫联合,有时候需要把几种不同类型的变量存放到同一段内存单元,例如,把一个整型变量、一个字符型变量、一个字符数组放在同一个地址开始的内存单元中。这三个变量在内存中占的字节数不同,但它们都从同一个地址开始,换句话说就是几个变量会互相覆盖。这种几个变量共同占用同一段内存的存储数据的方式,就叫共用体,这些变量也被称为共用体成员变量(简称“成员”)。
可以看到,共用体和结构体有些类似,但共用体中的成员会占用同一段内存(会相互覆盖),而结构体中的成员会分别占用不同的内存(不存在相互覆盖的问题)。看一看共用体定义的一般形式:

看如下代码:

也可以将类型定义和变量定义分开。例如,先定义一个共用体(有名字的共用体):

因为共用体有名字,所以可以用共用体的名字来定义共用体变量:

当然,在定义共用体的时候可以不给共用体命名,但这就需要在定义共用体的同时也一起定义属于该共用体类型的变量。看如下代码:

注意,从定义上看,共用体和前面讲的结构体非常类似,把共用体定义一般形式中的union替换成struct就是定义结构体,换回union就是定义共用体。但是结构体和共用体又明显不同:结构体占用的内存大小是各个成员占的内存大小之和,每个成员分别占用一段不同的内存。看如下结构体定义代码:

如果对这个结构用sizeof运算符做占用的内存大小计算,得到的结果是56,因为int类型占4字节,字符数组占52字节:

这里还需要额外进行一些说明,C语言存在结构体成员的字节对齐问题(对齐的原因是基于硬件或者效率,例如有些成员占2字节,但是为了运行效率,系统可能会额外多分配出2字节来),所以用sizeof运算符计算student结构体所占用的内存大小,得到的结果可能不是56,可能比56要多,但绝对不会低于56。字节对齐问题如果笔者有兴趣可以通过搜索引擎来了解。
而共用体因为成员占用同一段内存,所以占用的内存大小等于占用内存最大的成员所占的内存大小,而不是每个成员所占内存大小之和:

有几点说明:
(1)共用体变量的引用方式。和结构体很类似,不能直接引用共用体变量,只能引用共用体变量中的成员,如a.cname、a.carnum,要知道,a对应的内存空间中有好几种不同类型的成员,每个成员占的内存大小都可能不同,所以必须明确写明引用的成员。
(2)共用体变量的特点。同一段内存中存放几个不同类型的成员,但每一个瞬间只能存放其中一个,换句话说,每个瞬间只能有一个成员起作用,其他成员不起作用。
程序中最后给哪个成员赋值,哪个成员就起作用。看如下代码:

所以,使用共用体变量时必须时刻注意当前存放在其中的数据,明确知道哪个成员当前正在起作用。
(3)共用体变量地址和其成员的地址都相同。也就是说,&a、&a.carnum、&a.cartype、&a.cname所代表的首地址都相同,共用体变量名也代表共用体变量的首地址,这一点与数组名代表数组首地址的说法类似。
(4)共用体变量不能在定义的时候给所有成员都进行初始化。看如下代码:

但是在定义的时候初始化第一个成员是允许的。看如下代码:

共用体应用的场合不多,这里不做太多尝试和探讨。