8.3 共用体union
8.3.1 概述
和struct极其类似,union也是用来构造或说明一种新的数据类型的关键字。在语法形式方面,两者极其相似,但在语法含义上却南辕北辙。
struct数据类型是将几个数据聚合成了一个整体,而union这种数据类型则为几个数据提供了共用的存储空间。所以,很显然,union数据类型也有若干成员。
union类型的声明:
struct的各个成员在内存中各自有自己独立的存储空间,而union的各个成员占据的内存空间的起点位置是一样的,union类型的各个成员可以重叠地轮流放置在一处。因此union占据的空间大于或等于其最大成员所需要的空间。对于union类型的量来说,在任一时刻只保存着一个成员的值。union提供的是在同一个空间操作不同类型数据的能力。下面的例子给出了一个声明联合体数据类型的示范。
这个数据类型声明所声明的是一个union数据类型,这种数据类型的量具有3个数据成员(i、c、ll),这3个数据成员的存储空间的起点是相同的,如图8-3所示。
图8-3 共用体变量存储示意
union类型也以“union类用体标记”,作为数据类型的名称来定义变量。如:
union u_t u1, u2;
在定义union类型变量时可以赋初值,但是只能对第一个成员进行这种初始化。如
与struct类型一样,union类型量也能够进行“.”(成员访问)等运算。union类型变量也能够被“=”(赋值)。
union类型量的含义完全取决于上一次向其中写入了什么,而每次写入总会“擦去”些原来的内容。这样在各个不同的时刻,union类型量的含义可能并不同。所以编程者需要特别注意把握每个时刻union类型量的具体意义。这可以通过额外增加状态变量等手段实现。
通常的编程问题中,用到union类型量的机会并不多。但是,利用这种数据类型,可以帮助我们深入地考察数据的存储结构。
8.3.2 对double类型的解析
C语言仅仅描述了double类型的存储模型,但是并没有对这种类型的存储方式做出硬性规定。在常见的计算机中,double类型基本是依据ISO/IEEE Std 754-1985标准的规范存储的。这个标准中的浮点数存储的主要规定是:把浮点数化成小数点前只有一位1的小数与2的指数的乘积的形式,存储小数点后的52位、2的指数及数的符号。下面就借助union类型对double类型数据做一番考察。
1.另眼看浮点
为了了解double类型在内存中的存储方式,必须考察它的每一个字节。C语言的double类型本身并没有提供这种可能,但好在union类型提供了这种可能性。如果设计一个下面那样的数据类型,实际上就相当于获得了另一种观察double类型的角度。
代码一点也不难写。
程序代码8-9
输出如图8-4所示。
图8-4 另眼看浮点
2.对结果的解读
在解读“8d 97 6e 12 83 c0 f3 3f”这个结果之前,必须首先了解一些必要的硬件知识。
在计算机中,数据的读写方向,有的是从左到右(由高到低),有的是从右到左(由低到高)。此外历史上也有过更奇怪的高低次序。输出前面结果的计算机恰好属于从右到左(由低到高)那种。因此为了和我们平时书写的顺序一致,“8d 97 6e 12 83 c0 f3 3f”这个结果应该反转一下次序,改为“3f f3 c0 83 12 6e 97 8d”,这样就和我们平时的习惯一致了。
“3f f3 c0 83 12 6e 97 8d”,的二进制为
这个二进制数的后52位为“0011 1100 0000 1000 0011 0001 0010 0110 1110 1001 0111 1000 1101”,实际表示的是“1.0011 1100 0000 1000 0011 0001 0010 0110 1110 1001 0111 1000 1101”。稍加演算,不难计算出,这是十进制的“1.2344999999999999307220832633902”。这里再次演示了double类型的近似性。
再看这个二进制数的其余部分。这个二进制数前面的“0011 1111 1111”中最前面的那个“0”表示这是一个正的浮点数,余下的部分记录指数。
“011 1111 1111”的值恰好为1023。所谓“恰好”是因为2的指数都是加上1023后写入内存的。这就说明原来的指数是0。因此
这个结果同所介绍的模型完全一致。