轉載轉載!原出處http://blog.csdn.net/huqinwei987/article/details/23597091
有些基礎知識快淡忘了,所以有必要複習一遍,在不借助課本死知識的前提下做些推理判斷,溫故知新。
union,中文名“聯合體、共用體”,在某種程度上類似結構體struct的一種數據結構,共用體(union)和結構體(struct)同樣可以包含很多種數據類型和變量。
結構體(struct)中所有變量是“共存”的——優點是“有容乃大”,全面;缺點是struct內存空間的分配是粗放的,不管用不用,全分配。
而聯合體(union)中是各變量是“互斥”的——缺點就是不夠“包容”;但優點是內存使用更爲精細靈活,也節省了內存空間。
2.雙刃劍——多種訪問內存途徑共存
- //example
- #include<stdio.h>
- union var{
- long int l;
- int i;
- };
- main(){
- union var v;
- v.l = 5;
- printf("v.l is %d\n",v.i);
- v.i = 6;
- printf("now v.l is %ld! the address is %p\n",v.l,&v.l);
- printf("now v.i is %d! the address is %p\n",v.i,&v.i);
- }
- 結果:
- v.l is 5
- now v.l is 6! the address is 0xbfad1e2c
- now v.i is 6! the address is 0xbfad1e2c
上例中我改了v.i的值,結果v.l也能讀取,那麼也許我還以爲v.l是我想要的值呢,因爲上邊提到了union的內存首地址肯定是相同的,那麼還有一種情況和上邊類似:
這種邏輯上的錯誤是很難找出來的(只有當數據類型相去甚遠的時候稍好,出個亂碼什麼的很容易發現錯誤)。
3.聯合體union和大小端(big-endian、little-endian):
- #include<stdio.h>
- union var{
- char c[4];
- int i;
- };
- int main(){
- union var data;
- data.c[0] = 0x04;//因爲是char類型,數字不要太大,算算ascii的範圍~
- data.c[1] = 0x03;//寫成16進製爲了方便直接打印內存中的值對比
- data.c[2] = 0x02;
- data.c[3] = 0x11;
- //數組中下標低的,地址也低,按地址從低到高,內存內容依次爲:04,03,02,11。總共四字節!
- //而把四個字節作爲一個整體(不分類型,直接打印十六進制),應該從內存高地址到低地址看,0x11020304,低位04放在低地址上。
- printf("%x\n",data.i);
- }
結果:
11020304
證明我的32位linux是小端(little-endian)
- #include<stdio.h>
- union sizeTest{
- int a;
- double b;
- };
- main(){
- union sizeTest unionA;
- union sizeTest unionB;
- union sizeTest unionC;
- printf("the initial address of unionA is %p\n",&unionA);
- printf("the initial address of unionB is %p\n",&unionB);
- printf("the initial address of unionC is %p\n",&unionC);
- }
打印,可以看到結果:
the initial address of unionA is 0xbf9b8df8
the initial address of unionB is 0xbf9b8e00
the initial address of unionC is 0xbf9b8e08
很容易看出,8,0,8,這間隔是8字節,按double走的。
怕不保險,再改一下,把int改成數組,其他不變:
打印
the initial address of unionA is 0xbfbb7738
the initial address of unionB is 0xbfbb7760
the initial address of unionC is 0xbfbb7788
88-60=28
60-38=28
算錯了?我說的可是16進制0x。那麼0x28就是40個字節,正好是數組a的大小。
沒錯,union的成員變量是相當於開闢了幾個接口(即union包含的變量)!但是,沒開闢就不能用了?當然也能用!
- #include<stdio.h>
- union u{
- int i;
- double d;//這個union有8字節大小
- };
- main(){
- union u uu;
- uu.i = 10;
- printf("%d\n",uu.i);
- char * c;
- c = (char *)&uu;//把union的首地址賦值、強轉成char類型
- c[0] = 'a';
- c[1] = 'b';
- c[2] = 'c';
- c[3] = '\0';
- c[4] = 'd';
- c[5] = 'e';
- //最多能到c[7]
- printf("%s\n",c);//利用結束符'\0'打印字符串"abc"
- printf("%c %c %c %c %c %c\n",c[0],c[1],c[2],c[3],c[4],c[5]);
- }
有些東西,熟悉編譯原理和編譯器工作過程的話,解決會更容易點,雖然我現在這方面技能不太強,不過一般問題也足夠分析了。
補充:
補充1:
關於“有名”與“無名”聯合體在結構體內所佔空間的問題,其實這和是不是結構體無關,只和“有名”、“無名”有關,而且有名無名也是表象,其實是聲明類型與定義變量的區別,看例子,直接打印,
- #include <stdio.h>
- struct s1{
- union u{
- int i;
- };
- struct ss1{
- int i;
- };
- };
- struct s2{
- union{
- int i;
- };
- struct{
- int i;
- };
- };
- struct s3{//the same to s2
- union su3{
- int i;
- }su33;
- struct ss3{
- int i;
- }ss33;
- };
- union su4{
- int i;
- };
- struct ss4{
- int i;
- };
- struct s4{//the same to s3
- union su4 su44;
- struct ss4 ss44;
- };
- struct s5{//the same to s1
- union su4;
- struct ss4;
- };
- struct s6{//the same to s1
- union{
- int;
- };
- struct{
- int;
- };
- };
- main(){
- struct s1 sVal1;
- struct s2 sVal2;
- struct s3 sVal3;
- struct s4 sVal4;
- struct s5 sVal5;
- struct s6 sVal6;
- printf("sVal1's size:%d\n",sizeof(sVal1));
- printf("sVal1:%p\t%d\n",&sVal1);
- printf("sVal2's size:%d\n",sizeof(sVal2));
- printf("sVal2:%p\t%d\n",&sVal2);
- printf("sVal3's size:%d\n",sizeof(sVal3));
- printf("sVal3:%p\t%d\n",&sVal3);
- printf("sVal4's size:%d\n",sizeof(sVal4));
- printf("sVal4:%p\t%d\n",&sVal4);
- printf("sVal5's size:%d\n",sizeof(sVal5));
- printf("sVal5:%p\t%d\n",&sVal5);
- printf("sVal5's size:%d\n",sizeof(sVal5));
- printf("sVal5:%p\t%d\n",&sVal5);
- }
類型就是類型,和是不是結構體、聯合體無關的,你的“int i;”中i不就是個變量嗎?如果換成int;結果相同(這就是s6)。
s4和s5的做法能幫助排除干擾,將子結構體與聯合體聲明在外,內部直接引用,4是定義了變量,5什麼都沒做。
另外,這種做法編譯的時候GCC會給你在相應的行做出提示“union_with_name.c:49: 警告:沒有聲明任何東西”