早先的教科書稱為「聯合」,「聯合」也是一種變量。現在有些書刊資料將它稱為「共同體」。本節採用聯合一詞。
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