1
C语言程序设计
1.9.2 8.2 联  合

8.2 联  合

联合是一种与结构相类似的构造类型,联合与结构一样,可以包括不同类型和长度的数据。联合与结构的主要区别在于:联合类型变量所占内存空间不是各个成员所需存储空间字节数的总和,而是联合成员中需要存储空间最大的成员所要求的字节数。这是因为,C编译程序规定联合的各个成员共享一个公共存储空间。在任何给定的时刻,只能允许联合的一个成员驻留在联合中,而对结构而言,则是所有成员一直都驻留在结构中。

联合的定义方法与结构相同。

定义联合类型的一般形式是:

img646

假如要定义一个名为data的联合,并说明value为联合变量。这个变量在不同时刻,可以是一个字符(1个字节)、一个整数(2个字节)或一个长整数(4个字节)。这个联合可写成如下定义:

img647

对于上述定义,当编译程序看到关键字union时,它扫视联合定义中的成员类型表,找出要求占用最大存储空间的一个成员,并以这个成员所需要的存储空间作为分配给联合变量的存储空间,以保证能存放任何一个成员数据。图8.2.1说明了联合变量value在内存中分配的情况。

可见联合变量value是一个能在不同时刻合理地保存三种数据类型中的任何一种类型的变量。使用联合不仅比使用结构更能节省一些存储空间,而且增加了处理数据的灵活性。

为了访问联合的成员,可以使用与结构相同的方法,即使用点运算符“.”或箭头运算符“−〉”。如果直接对联合变量进行操作,使用“.”运算符,若联合变量通过一个指针来访问,使用箭头运算符。例如,将整数100赋给value变量的成员num,可以用以下语句:

  value.num=100;

img648

图8.2.1 联合变量的存储空间分配

此时,联合变量value为一个整型变量。

如果将字符A赋给value变量的成员ch,可以写成:

  value.ch='A';

此时,联合变量value变为一个字符变量。

由于value是一个共享变量,因此在编程时必须记住在联合中当前存储的是什么样的数据类型。如果要进行数值计算,那么用法要前后一致,即取回的类型一定是最近才存放的类型。如果以一种类型存放,而又以另一种类型取出,则可能发生错误。下面举一个例子,说明联合变量的正确访问方法。

例8-2 联合变量的正确访问方法。

程序如下:

img649

该程序虽然在编译连接时未出错,但运行时产生了两个错误结果,如运行结果的第2行和第4行所示。产生错误的原因是由于程序中的两个语句(第15行和第18行)在对联合成员的访问中,存入的数据类型与取出的数据类型不同。第一个错误是以整数存入,而以浮点数取出;第二个错误是以浮点数存入,而以整数取出。

这里sizeof()函数是取联合变量存放的最大成员的字节数。sizeof是一个单目操作符,它返回变量或括号中的类型标识符的字节长度。在程序运行阶段,它不管在value中当前实际包含的是什么成员变量,取出的字节数仅仅是它包含的最大变量占有的字节数。程序输出表明,结构value变量的长度为4个字节(即浮点变量成员占用的存储区),这与定义是一致的。

在实际应用中,联合可能出现在结构中,结构也可能出现在联合中,为了表示复杂的数据关系,联合和结构常常在一起使用,即联合可能包括结构成员,结构也可能包括联合成员。

下面的例子定义了一个有关记录书籍、杂志、文章方面信息的联合类型:union entry。例中使用了联合和结构的嵌套。当联合变量info表示书籍信息时,联合变量中包含书籍的作者姓名author[]和书籍名称title[]两个成员;当联合变量info表示论文信息时,联合变量中包含有论文的作者author[]、论文名称title[]和发表论文的刊物journal[]三个成员。

img650

在联合嵌套结构情况下,对结构成员访问的一般形式是:

  联合变量.结构变量名.结构成员

例如:

  info.book.title 和 info.artic.title

分别访问的是书籍名和论文名。

在结构嵌套联合情况下,对联合成员访问的一般形式是:

  结构变量名.联合变量名.联合成员名

例8-3 下面的程序说明在联合嵌套结构的情况下,对结构成员的访问方法,以及说明联合的所有成员占用同一个存储空间,而结构占用的存储空间是各成员各自占有的存储空间之总和的分配情况。

程序如下:

img651

img652

程序中定义了一个联合类型value,它包含有三个成员,一个整型变量int_val;一个双精度浮点型变量dbl_val,一个结构类型变量comp_val。结构变量包括有两个双精度浮点型变量re和im,形成了联合和结构的嵌套。程序的第17行定义number为联合类型value的变量。从第19~30行,用单目运算符sizeof求出联合变量number中各成员占有内存的字节数。从第31~36行,用单目运算符&求出各成员在内存中的起始地址。

程序的运行结果表明,结构变量comp_val是最大的联合成员,占用16个字节(运行结果的第4行),其中re变量和im变量各占8个字节。所以联合变量number占有的字节数就是16个字节(运行结果的第1行)。由于联合成员共享一个存储空间,所以,当联合中存储一个整数或双精度浮点数时,编译程序仅使用其中2个或8个字节。在联合中任何一个成员被使用时都是从同一地址单元开始存放的(本例为由无符号数65484表示的地址)。当联合中的结构成员被使用时,结构中的re变量和im变量是按占有的存储空间顺序分配的。由于各占8个字节,所以其起始地址分别为65484和65492。

必须指出的是,在程序设计中,利用联合变量的各成员共享公共存储区的特点,可方便地进行数据的交换和处理。下面的程序就是一个类似的例子,程序把用十六进制数表示的整型数(占2个字节)的低位字节和高位字节相互交换。数据从键盘输入,输出交换的结果。

例8-4 交换用十六进制数表示的整型数的低位字节和高位字节。

img653

img654

程序中定义了联合类型union body。a和b为联合变量。联合变量包括两个成员,一个为结构变量byte,结构变量包括了表示一个整型数的低位字节变量l和高位字节变量h,另一个联合成员为无符号整数变量word。由于a、b为联合变量,语句“scanf("%x",&a.word);”把从键盘读入的一个十六进制数放入word变量中。当执行下面两个赋值语句时,a.byte.l和a.byte.h分别取至word变量中读入数据的低位字节和高位字节。通过两个赋值语句,把联合变量a的两个成员a.byte.l和a.byte.h的值分别赋给了联合变量b的两个成员b.byte.h和b.byte.l,当最后一个语句以无符号整型成员输出b.word时,在b.word中已是经过交换的数据。