早先的教科书称为“联合”,“联合”也是一种变量。现在有些书刊资料将它称为“共同体”。本节采用联合一词。
1.使用的方法不对
联合的定义同结构的定义形式上相同,除了把关键字struct换成union之外,像联合名(联合标识符)、联合成员、联合变量等书写方法也都和结构相同。联合标识符是用来表示联合的类型名字。结构允许有字段成员,但字段不能做联合成员。
【10.1】如下程序给出一个奇怪的输出,找出原因并改正。
#include <stdio.h> union udate{ int uint ; long ulong ; float ufloat ; double udouble ; char *ustring ; } u ; void main ( ) { u.uint=1 ; u.ulong=100 ; u.ufloat=1.0 ; u.udouble=1.00 ; u.ustring=\"abc\" ; printf ( \"%12dn\" , u.uint ); printf ( \"%12dn\" , u.ulong ); printf ( \"%12fn\" , u.ufloat ); printf ( \"%12fn\" , u.udouble ); printf ( \"%12sn\" , u.ustring ); }
【解答】程序输出结果如下:
4341796 4341796 0.000000 1.000000 abc
这是对联合的使用错误。联合的各个元素是共享同一内存,赋值一个元素必须马上使用,否则就要把地址让出来供其他元素使用。这个程序一直赋值下去,最后的内容就是字符串abc。用不同数据类型输出abc,当然只有最后一个输出结果正确。
只要赋值一个马上输出一个即可。下面在程序中增加输出多个元素的地址以验证它们的地址相同。
1 00427B88 100 00427B88 1.000000 00427B88 1.000000 00427B88 abc 00427B88
同结构一样,联合定义只是描述了该联合的“模式”或“形态”,不能为联合分配存储空间;只有给出了联合变量后,编译程序才给其分配存储空间。
联合中各成员的大小不一,编译程序将对联合变量分配一个足以容纳其中最大一个成员的存储空间。在一定的时间内,只能有一个某种类型的数据存储在联合变量中。就是说,联合变量能够保存其成员的数值,但不能同时保存两个以上的不同成员的数值。联合所保存的数据,只是最后所赋给它的成员的值;也只有这个成员是有效的,其他成员均无效。可见,联合的每个成员的地址就是联合变量的地址。因为它们共有这个地址,所以又称为共同体。下面的例子说明了它们共用地址的问题。
这一点与结构不同。结构是把所有成员的数值存储在对应变量中。下面的例子说明在结构中各成员占有不同的位置,而联合却具有同一位置。
【10.2】演示系统对联合与结构元素分配内存的区别。
#include <stdio.h> union uda { char uch ; int uin ; long ulo ; } u ; struct sda { char sch ; int sin ; long slo ; struct sda *next ; } s ; void main ( ) { printf ( \"address of u :%pn\" , &u ); printf ( \"address of uch :%pn\" , &u.uch ); printf ( \"address of uin :%pn\" , &u.uin ); printf ( \"address of ulo :%pn\" , &u.ulo ); putchar (\'n\' ); printf ( \"address of s :%pn\" , &s ); printf ( \"address of sch :%pn\" , &s.sch ); printf ( \"address of sin :%pn\" , &s.sin ); printf ( \"address of ulo :%pn\" , &s.slo ); printf ( \"addressofnext :%pn\" , &s.next ); }
输出结果如下:
address of u :00427B84 address of uch :00427B84 address of uin :00427B84 address of ulo :00427B84 address of s :00427BD0 address of sch :00427BD0 address of sin :00427BD4 address of ulo :00427BD8 addressofnext :00427BDC
从上面的执行结果不难看出,在结构中,第一个成员的地址就是结构的地址,结构成员具有自己的地址,而联合却只有一个公共地址。
2.典型的使用的方法
联合可以出现在结构和数组中,数组和结构也可以出现在联合中。目前对联合的操作只允许存取成员和取其地址。由于联合各成员的地址就是联合变量的地址,故联合变量的数据的存取与结构不同,只能通过成员进行,且一次只能存取一个成员,不能直接存取联合变量;不能在定义时对联合变量进行初始化。与结构相同之处就是可以把联合作为参数传递给函数,也可以从函数返回联合;可以使用指向联合的指针;对于指向联合的指针,也使用成员运算符的略写形式“->”。如下面的例子所示。
【例10.3】使用指向联合的指针的例子。
#include <stdio.h> void func ( struct uda* ); #define INT 1 #define LONG 2 #define FLOAT 3 #define DOUBLE 4 #define STRING 5 struct uda{ union { int uin ; long ulo ; float uf ; double udo ; char *ust ; } u ; int type ; } x ; void main ( ) { x.type=1 ; x.u.uin=123 ; func (&x ); x.type=2 ; x.u.ulo=123456789 ; func (&x ); x.type=3 ; x.u.uf=1.5 ; func (&x ); x.u.udo=12.03 ; x.type=4 ; func (&x ); x.u.ust=\"Go home !\" ; x.type=5 ; func (&x ); } void func ( struct uda *b ) { switch ( b->type ) { case INT : printf ( \"%dn\" ,b-> u.uin ); break ; case LONG : printf ( \"%ldn\" ,b-> u.ulo ); break ; case FLOAT : printf ( \"%fn\" ,b-> u.uf ); break ; case DOUBLE : printf ( \"%lfn\" ,b-> u.udo ); break ; case STRING : printf ( \"%sn\" ,b-> u.ust ); } }
运行结果如下:
123 123456789 1.500000 12.030000 Go home !
在程序中,函数func是用来管理联合变量所保存数据的类型的。该程序的开头有宏定义。它们定义了表示联合成员类型的标号,接下去是结构定义语句。结构的成员type是用来管理联合变量和联合成员的int型变量。给它赋以由#define所定义的值后,便可识别联合变量现在所能使用的成员。
在函数func中,型为uda的结构的指针b以参数形式传递。在switch语句中,选择带有对应联合目前所保存的成员参数的函数printf。数据类型不同,函数printf的格式控制字符也就不同。
从这个程序中还可以看出,在引用结构中联合成员时,函数printf的参数写法与引用结构中的结构成员时相同。由此可见,在用法上,联合与结构的确是完全相同的。
【例10.4】一个一维数组和一个二维数组同处一个联合,将数据输入一维数组后,在二维数组中输出。
#include <stdio.h> union data{ int a[10] ; int b[2][5] ; } ; void main () { union data ab ; int i ,j ; for (i=0 ;i<10 ;i++ ) // 置入11 12 13 14 15 16 17 18 19 20 ab.a[i]=11+i ; for (i=0 ;i<2 ;i++ ) for (j=0 ;j<5 ;j++ ) printf (\"%3d\" ,ab.b[i][j] ); printf (\"n\" ); }
程序输出:
11 12 13 14 15 16 17 18 19 20