# 共用体 通过前面的讲解,我们知道结构体(Struct)是一种构造类型或复杂类型,它可以包含多个类型不同的成员。在C语言中,还有另外一种和结构体非常类似的语法,叫做**共用体(Union)**,共用体有时也被称为联合或者联合体,这也是 Union 这个单词的本意。它的定义格式为: ``` union 共用体名{ 成员列表; }; ``` 结构体和共用体的区别在于:结构体的各个成员会占用不同的内存空间,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。 结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。 共用体也是一种自定义类型,可以通过它来创建变量,例如: ```c union data{ int n; char ch; double f; }; union data a, b, c; //上面是先定义共用体,再创建变量,也可以在定义共用体的同时创建变量 union data{ int n; char ch; double f; } a, b, c; //如果不再定义新的变量,也可以将共用体的名字省略 union { int n; char ch; double f; } a, b, c; ``` 共用体 data 中,成员 f 占用的内存最多,为 8 个字节,所以 data 类型的变量(也就是 a、b、c)也占用 8 个字节的内存,请看下面的演示: ```c #include union data{ int n; char ch; short m; }; int main(){ union data a; printf("%d, %d\n", sizeof(a), sizeof(union data) ); a.n = 0x40; printf("%X, %c, %hX\n", a.n, a.ch, a.m); a.ch = '9'; printf("%X, %c, %hX\n", a.n, a.ch, a.m); a.m = 0x2059; printf("%X, %c, %hX\n", a.n, a.ch, a.m); a.n = 0x3E25AD54; printf("%X, %c, %hX\n", a.n, a.ch, a.m); return 0; } ``` 这段代码不但验证了共用体的长度,还说明共用体成员之间会相互影响,修改一个成员的值会影响其他成员。 ## 共用体的应用 共用体在一般的编程中应用较少,在单片机中应用较多。 ## 大端小端以及判别方式 大端和小端是指数据在内存中的存储模式,它由 CPU 决定: 1) 大端模式(Big-endian)是指将数据的低位(比如 1234 中的 34 就是低位)放在内存的高地址上,而数据的高位(比如 1234 中的 12 就是高位)放在内存的低地址上。这种存储模式有点儿类似于把数据当作字符串顺序处理,地址由小到大增加,而数据从高位往低位存放。 2) 小端模式(Little-endian)是指将数据的低位放在内存的低地址上,而数据的高位放在内存的高地址上。这种存储模式将地址的高低和数据的大小结合起来,高地址存放数值较大的部分,低地址存放数值较小的部分,这和我们的思维习惯是一致,比较容易理解。 **为什么有大小端模式之分** 计算机中的数据是以字节(Byte)为单位存储的,每个字节都有不同的地址。现代 CPU 的位数(可以理解为一次能处理的数据的位数)都超过了 8 位(一个字节),PC机、服务器的 CPU 基本都是 64 位的,嵌入式系统或单片机系统仍然在使用 32 位和 16 位的 CPU。 对于一次能处理多个字节的CPU,必然存在着如何安排多个字节的问题,也就是大端和小端模式。以 int 类型的 0x12345678 为例,它占用 4 个字节,如果是小端模式(Little-endian),那么在内存中的分布情况为(假设从地址 0x 4000 开始存放): | 内存地址 | 0x4000 | 0x4001 | 0x4002 | 0x4003 | | -------- | ------ | ------ | ------ | ------ | | 存放内容 | 0x78 | 0x56 | 0x34 | 0x12 | 如果是大端模式(Big-endian),那么分布情况正好相反: | 内存地址 | 0x4000 | 0x4001 | 0x4002 | 0x4003 | | -------- | ------ | ------ | ------ | ------ | | 存放内容 | 0x12 | 0x34 | 0x56 | 0x78 | 我们的 PC 机上使用的是 X86 结构的 CPU,它是小端模式;51 单片机是大端模式;很多 ARM、DSP 也是小端模式(部分 ARM 处理器还可以由硬件来选择是大端模式还是小端模式)。 **如何判别CPU是大端模式还是小端模式** 借助共用体,我们可以检测 CPU 是大端模式还是小端模式,请看代码: ```c #include int main(){ union{ int n; char ch; }data; data.n = 0x0001; //也可以直接写data.n=1 if(data.ch == 1){ printf("CPU mode: Little-endian.\n"); }else{ printf("CPU mode: Big-endian.\n"); } return 0; } ``` 在PC机上的运行结果: Little-endian 共用体的各个成员是共用一段内存的。1 是数据的低位,如果 1 被存储在 data 的低字节,就是小端模式,这个时候 data.ch 的值也是 1。如果 1 被存储在 data 的高字节,就是大端模式,这个时候 data.ch 的值就是 0。