日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

結(jié)構(gòu)體struct和聯(lián)合體union最全講解...

 beginnow1 2021-09-24

摘要:首先感謝三位博主,并做出總結(jié).

首先了解一下struct的儲存結(jié)構(gòu):

一、結(jié)構(gòu)體的訪問

1.結(jié)構(gòu)體成員的的直接訪問,如下結(jié)構(gòu)體:

struct  A{

           int a;

           long *b;

           char c[20];

};

struct A   com;

結(jié)構(gòu)體成員通過操作符"."訪問,表達式com.a的結(jié)果是個數(shù)組名,可以把它使用在任何可以使用數(shù)組名的地方,com.a[4],將選擇一個數(shù)組元素。

2、結(jié)構(gòu)體成員的間接訪問

struct A *p;

可以使用(*p).a訪問結(jié)構(gòu)體成員,但這種形式有點不簡潔所以使用操作符"->"來訪問結(jié)構(gòu)體成員,箭頭操作符對左操作數(shù)執(zhí)行間接訪問來獲取指針所指向的結(jié)構(gòu),然后根據(jù)右操作數(shù)來訪問一個成員,p->a。

二、結(jié)構(gòu)體的自引用

struct B{

         int a;

         struct B b;

         int c;

};

這種引用是不合法的,因為b是一個完整的結(jié)構(gòu),第二個成員又是另一個完整的結(jié)構(gòu),還包括她自己的成員,這樣會循環(huán)下去無法及所結(jié)構(gòu)體的大小。

struct B{

         int a;

         struct B  *b;

         int c;

};

這種聲明是合法的,b現(xiàn)在是一個指針它所占的字節(jié)數(shù)是已知的,可以計算出結(jié)構(gòu)體的大小,這種自引用是合法的。

三、結(jié)構(gòu)體、指針和成員

typedef   struct{

    int a;

    short b[2];

}Ex1;

typedef   struct{

    int a;

    char b[3];

    Ex1 c;

    struct Ex1 d;

}Ex2;

Ex2 x={1,"My",{2,{3,4}},0};

Ex2 *p=&x;

如下圖來表示此結(jié)構(gòu):

創(chuàng)建一個指向整型的指針:int *p1;若要使它指向整型成員a,應(yīng)使用&取得一個指向p->a的指針:p1=&p->a.

訪問嵌套的結(jié)構(gòu)體:p->c.a即為訪問c結(jié)構(gòu)體中的整形a。

訪問指針成員:定義另一結(jié)構(gòu):Ex y;     x.d=&y;則Ex->d->c.b[1]=4;則表示如下圖空間:

四、結(jié)構(gòu)體的內(nèi)存對齊

對齊規(guī)則

每個特定平臺上的編譯器都有自己的默認“對齊系數(shù)”,也可以通過預(yù)編譯命令#pragma pack(n),n=1,2,4,8,16來改變這一系數(shù),其中的n就是你要指定的“對齊系數(shù)”。
規(guī)則:
1、數(shù)據(jù)成員對齊規(guī)則:struct數(shù)據(jù)成員,第一個數(shù)據(jù)成員放在offset為0的地方,以后每個數(shù)據(jù)成員的對齊按照#pragma pack指定的數(shù)值和這個數(shù)據(jù)成員自身長度中,比較小的那個進行。(即第一個數(shù)據(jù)成員以后的成員的偏移地址為#pragma pack指定的數(shù)值和這個數(shù)據(jù)成員自身長度中,比較小的那個的整數(shù)倍)
2、struct的整體對齊規(guī)則:在數(shù)據(jù)成員完成各自對齊之后,結(jié)構(gòu)體本身也要進行對齊,對齊將按照#pragma pack指定的數(shù)值和struct最大數(shù)據(jù)成員長度中,比較小的那個進行。
3、當(dāng)#pragma pack的n值等于或超過所有數(shù)據(jù)成員長度的時候,這個n值的大小將不產(chǎn)生任何效果。
     當(dāng)結(jié)構(gòu)體里面包含另一結(jié)構(gòu)體時,直接將該結(jié)構(gòu)體中的內(nèi)容代換進去,計算其總的存儲空間即可。

例:(在此平臺上int占4個字節(jié))

雖然A和A1所包含的成員相同,但A占了12個字節(jié),A1占8個字節(jié),所以在聲明中對結(jié)構(gòu)體列表的排列,因該讓邊界要求嚴格的成員首先出現(xiàn)(數(shù)據(jù)成員自生長度大的先出現(xiàn))

以上轉(zhuǎn)載自:https://www.cnblogs.com/Blog-day/p/MY_Blog_Days-3.html

1.聯(lián)合體union的基本特性——和struct的同與不同

union,中文名“聯(lián)合體、共用體”,在某種程度上類似結(jié)構(gòu)體struct的一種數(shù)據(jù)結(jié)構(gòu),共用體(union)和結(jié)構(gòu)體(struct)同樣可以包含很多種數(shù)據(jù)類型和變量。

不過區(qū)別也挺明顯:

結(jié)構(gòu)體(struct)中所有變量是“共存”的——優(yōu)點是“有容乃大”,全面;缺點是struct內(nèi)存空間的分配是粗放的,不管用不用,全分配。

而聯(lián)合體(union)中是各變量是“互斥”的——缺點就是不夠“包容”;但優(yōu)點是內(nèi)存使用更為精細靈活,也節(jié)省了內(nèi)存空間。

 

 

2.雙刃劍——多種訪問內(nèi)存途徑共存

一個例子了然:

 

  1. //example  
  2. #include<stdio.h>  
  3. union var{  
  4.         long int l;  
  5.         int i;  
  6. };  
  7. main(){  
  8.         union var v;  
  9.         v.l = 5;  
  10.         printf("v.l is %d\n",v.i);  
  11.         v.i = 6;  
  12.         printf("now v.l is %ld! the address is %p\n",v.l,&v.l);  
  13.         printf("now v.i is %d! the address is %p\n",v.i,&v.i);  
  14. }  
  15. 結(jié)果:  
  16. v.l is 5  
  17. now v.l is 6! the address is 0xbfad1e2c  
  18. now v.i is 6! the address is 0xbfad1e2c  

 

所以說,管union的叫共用體還真是貼切——完全就是共用一個內(nèi)存首地址,并且各種變量名都可以同時使用,操作也是共同生效。如此多的access內(nèi)存手段,確實好用,不過這些“手段”之間卻沒法互相屏蔽——就好像數(shù)組+下標和指針+偏移一樣。

上例中我改了v.i的值,結(jié)果v.l也能讀取,那么也許我還以為v.l是我想要的值呢,因為上邊提到了union的內(nèi)存首地址肯定是相同的,那么還有一種情況和上邊類似:

一個int數(shù)組變量a,一個long int(32位機中,long int占4字節(jié),與int相同)變量b,我即使沒給int變量b賦值,因為數(shù)據(jù)類型相同,我使用int變量b也完全會拿出int數(shù)組a中的a[0]來,一些時候一不小心用上,還以為用的就是變量b呢~

這種邏輯上的錯誤是很難找出來的(只有當(dāng)數(shù)據(jù)類型相去甚遠的時候稍好,出個亂碼什么的很容易發(fā)現(xiàn)錯誤)。

 

PS:感謝熱心網(wǎng)友的提醒“在union定義結(jié)束時加分號”,其實是可以不加的,因為他不在主函數(shù)內(nèi),不是執(zhí)行的語句,如果是主函數(shù)內(nèi)聲明的union就必須加分號了,在主函數(shù)內(nèi)不加分號就涉及到基礎(chǔ)常識了——沒有分號隔開怎能叫一句。

 

3.聯(lián)合體union和大小端(big-endian、little-endian):


  1. #include<stdio.h>  
  2. union var{  
  3.         char c[4];  
  4.         int i;  
  5. };  
  6.   
  7. int main(){  
  8.         union var data;  
  9.         data.c[0] = 0x04;//因為是char類型,數(shù)字不要太大,算算ascii的范圍~  
  10.         data.c[1] = 0x03;//寫成16進制為了方便直接打印內(nèi)存中的值對比  
  11.         data.c[2] = 0x02;  
  12.         data.c[3] = 0x11;  
  13. //數(shù)組中下標低的,地址也低,按地址從低到高,內(nèi)存內(nèi)容依次為:04,03,02,11??偣菜淖止?jié)!  
  14. //而把四個字節(jié)作為一個整體(不分類型,直接打印十六進制),應(yīng)該從內(nèi)存高地址到低地址看,0x11020304,低位04放在低地址上。  
  15.         printf("%x\n",data.i);  
  16. }  


結(jié)果:
11020304
證明我的32位linux是小端(little-endian)

 

 


4.聯(lián)合體union所占內(nèi)存空間大小:

前邊說了,首先,union的首地址是固定的,那么,union到底總共有多大?根據(jù)一些小常識,做個不嚴謹不高深的基礎(chǔ)版驗證吧。

根據(jù):分配??臻g的時候內(nèi)存地址基本上是連續(xù)的,至少同類型能保證在一起,連續(xù)就說明,我如果弄三個結(jié)構(gòu)體出來,他們?nèi)齻€地址應(yīng)該連著,看一下三個地址的間隔就知道了。

  1. #include<stdio.h>  
  2. union sizeTest{  
  3.         int a;  
  4.         double b;  
  5. };  
  6. main(){  
  7.         union sizeTest unionA;  
  8.         union sizeTest unionB;  
  9.         union sizeTest unionC;  
  10.   
  11.         printf("the initial address of unionA is %p\n",&unionA);  
  12.         printf("the initial address of unionB is %p\n",&unionB);  
  13.         printf("the initial address of unionC is %p\n",&unionC);  
  14. }  



打印,可以看到結(jié)果:

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字節(jié),按double走的。

怕不保險,再改一下,把int改成數(shù)組,其他不變:


  1. union sizeTest{  
  2.         int a[10];  
  3.         double b;  
  4. };  


此時sizeTest大小為16字節(jié),因為需要當(dāng)數(shù)組為a[10] size大小為10,取較小值的最小公倍數(shù).8*2 = 16. 


5.聯(lián)合體union適用場合:

有了前邊那個驗證,基本可以確認,union的內(nèi)存是照著里邊占地兒最大的那個變量分的。

也就可以大膽的推測一下,這種union的使用場合,是各數(shù)據(jù)類型各變量占用空間差不多并且對各變量同時使用要求不高的場合(單從內(nèi)存使用上,我覺得沒錯)。

像上邊做的第二個測試,一個數(shù)組(或者更大的數(shù)組int a[100]),和一個或者幾個小變量寫在一個union里,實在沒什么必要,節(jié)省的空間太有限了,還增加了一些風(fēng)險(最少有前邊提到的邏輯上的風(fēng)險)。所以,從內(nèi)存占用分析,這種情況不如直接struct。

 

不過話說回來,某些情況下雖然不是很節(jié)約內(nèi)存空間,但是union的復(fù)用性優(yōu)勢依然存在啊,比如方便多命名,這種“二義性”,從某些方面也可能是優(yōu)勢。這種方法還有個好處,就是某些寄存器或通道大小有限制的情況下,可以分多次搬運。

 

 

6.本質(zhì)&進階:

 

根據(jù)union固定首地址union按最大需求開辟一段內(nèi)存空間兩個特征,可以發(fā)現(xiàn),所有表面的定義都是虛的,所謂聯(lián)合體union,就是在內(nèi)存給你劃了一個足夠用的空間,至于你怎么玩~它不管~?。ê沃故莡nion和struct,C不就是玩地址么,所以使用C靈活,也容易犯錯)

 

沒錯,union的成員變量是相當(dāng)于開辟了幾個接口(即union包含的變量)!但是,沒開辟就不能用了?當(dāng)然也能用!

寫個小測試:

  1. #include<stdio.h>  
  2. union u{  
  3.         int i;  
  4.         double d;//這個union有8字節(jié)大小  
  5. };  
  6. main(){  
  7.         union u uu;  
  8.         uu.i = 10;  
  9.         printf("%d\n",uu.i);  
  10.   
  11.         char * c;  
  12.         c = (char *)&uu;//把union的首地址賦值、強轉(zhuǎn)成char類型  
  13.         c[0] = 'a';  
  14.         c[1] = 'b';  
  15.         c[2] = 'c';  
  16.         c[3] = '\0';  
  17.         c[4] = 'd';  
  18.         c[5] = 'e';  
  19. //最多能到c[7]  
  20.         printf("%s\n",c);//利用結(jié)束符'\0'打印字符串"abc"  
  21.         printf("%c %c %c %c %c %c\n",c[0],c[1],c[2],c[3],c[4],c[5]);  
  22. }  

一個例子了然,我的結(jié)構(gòu)體只定義了int和double“接口”,只要我獲得地址,往里邊扔什么數(shù)據(jù)誰管得到?這就是C語言的強大,這就是union的本質(zhì)——只管開辟一段空間。

有些東西,熟悉編譯原理和編譯器工作過程的話,解決會更容易點,雖然我現(xiàn)在這方面技能不太強,不過一般問題也足夠分析了。

以上轉(zhuǎn)自:https://www.cnblogs.com/tianlangshu/p/5204521.html.

結(jié)構(gòu)體的嵌套問題

結(jié)構(gòu)體的自引用(self reference),就是在結(jié)構(gòu)體內(nèi)部,包含指向自身類型結(jié)構(gòu)體的指針。

結(jié)構(gòu)體的相互引用(mutual reference),就是說在多個結(jié)構(gòu)體中,都包含指向其他結(jié)構(gòu)體的指針。

1. 自引用結(jié)構(gòu)體

1.1 不使用typedef時

錯誤的方式:

struct tag_1{
    struct tag_1 A;  
    int value;
};

        這種聲明是錯誤的,因為這種聲明實際上是一個無限循環(huán),成員A是一個結(jié)構(gòu)體,A的內(nèi)部還會有成員是結(jié)構(gòu)體,依次下去,無線循環(huán)。在分配內(nèi)存的時候,由于無限嵌套,也無法確定這個結(jié)構(gòu)體的長度,所以這種方式是非法的。

正確的方式: (使用指針

struct tag_1{
    struct tag_1 *A; 
    int value;
};

        由于指針的長度是確定的(在32位機器上指針長度為4),所以編譯器能夠確定該結(jié)構(gòu)體的長度。

1.2 使用typedef 時

錯誤的方式:

typedef struct {
    int value;
    NODE *link; 
} NODE;

  這里的目的是使用typedef為結(jié)構(gòu)體創(chuàng)建一個別名NODEP。但是這里是錯誤的,因為類型名的作用域是從語句的結(jié)尾開始,而在結(jié)構(gòu)體內(nèi)部是不能使用的,因為還沒定義。

正確的方式:有三種,差別不大,使用哪種都可以。

復(fù)制代碼
typedef struct tag_1{
    int value;
    struct tag_1 *link; 
} NODE;


struct tag_2;
typedef struct tag_2 NODE;
struct tag_2{
    int value;
    NODE *link;   
};


struct tag_3{
    int value;
    struct tag_3 *link; 
};
typedef struct tag_3 NODE;
復(fù)制代碼

 

2. 相互引用 結(jié)構(gòu)體

錯誤的方式:

復(fù)制代碼
typedef struct tag_a{
    int value;
    B *bp; 
} A;

typedef struct tag_b{
    int value;
    A *ap;
} B;
復(fù)制代碼

       錯誤的原因和上面一樣,這里類型B在定義之前 就被使用。

正確的方式:(使用“不完全聲明”)

復(fù)制代碼
struct tag_a{
    struct tag_b *bp; 
    int value;
};
struct tag_b{
    struct tag_a *ap;
    int value;
};
typedef struct tag_a A;
typedef struct tag_b B;



struct tag_a;  
struct tag_b;
typedef struct tag_a A;
typedef struct tag_b B;
struct tag_a{
    struct tag_b *bp; 
    int value;
};
struct tag_b{
    struct tag_a *ap;
    int value;
};
復(fù)制代碼

 

嵌套結(jié)構(gòu)體時應(yīng)注意:

結(jié)構(gòu)體的自引用中,如下這種情況是非法的
struct s_ref {
 int a;
 struct s_ref b;
 char c;
};
因為結(jié)構(gòu)體內(nèi)部又包含自身結(jié)構(gòu)體類型b,這個長度不能確定,只能向下再查找,又包含自身結(jié)構(gòu)體類型b,又再向下查找,如此循環(huán),類似于永無出口的遞歸調(diào)用,是非法的。

但很多時候,的確需要使用到自引用,有個技巧,如下:
struct s_ref {
 int a;
 struct s_ref *b;  //注意這句與上面相同位置的區(qū)別
 char c;
};
這是合法的,因為此處是定義了一個指向結(jié)構(gòu)體的指針,指針的大小在具體的機器平臺和編譯器環(huán)境中都是已知的(即使不同的平臺環(huán)境的定義不完全相同)。所以不會導(dǎo)致上述的遞歸死循環(huán)。是合法和可行的。但是要提醒的是:這個指針看似指向自身,其實不是,而是指向同一類型的不同結(jié)構(gòu)。
鏈表和樹的數(shù)據(jù)結(jié)構(gòu)就都使用到此技巧。自身的結(jié)構(gòu)體指針指向下一節(jié)點或者下一子樹的地址。

這里有一種情況值得注意:
typedef struct {   //這里是結(jié)構(gòu)體類型定義
 int a;
 s_ref *b;  //注意這句引用了結(jié)構(gòu)體類型名
 char c;
}s_ref ;
這個結(jié)構(gòu)體類型定義是為了定義類型名s_ref,但卻失敗了。因為結(jié)構(gòu)體中就引用了結(jié)構(gòu)類型名,而此時還沒定義類型名。
可以改為如下:
typedef struct s_ref_t{   //這里是結(jié)構(gòu)體類型定義和結(jié)構(gòu)體標簽
 int a;
 struct s_ref_t *b;  //注意這句與上面相同位置的區(qū)別,使用了標簽
 char c;
}s_ref ;
這里將運行良好。


以上轉(zhuǎn)自:http://www.cnblogs.com/renyuan/archive/2012/11/30/2796792.html.

下面有個測試小程序:(基本上涵蓋了所有的情況)幫助你更好的理解結(jié)構(gòu)體和聯(lián)合體占用的內(nèi)存大小.

  1. #include <stdio.h>
  2. void main()
  3. {
  4. typedef struct{
  5. int a;
  6. short b;
  7. }struct_1;
  8. typedef struct_1* struct_0;
  9. typedef struct{
  10. int a;
  11. char b;
  12. struct_1 s1;
  13. }struct_2;
  14. typedef struct{
  15. int a;
  16. char b;
  17. long int c;
  18. struct_1 s1;
  19. }struct_3;
  20. typedef struct{
  21. int a;
  22. char b;
  23. struct_1 * s1;
  24. }struct_4;
  25. typedef struct{
  26. struct_1 * s1;
  27. }struct_5;
  28. typedef struct{
  29. struct_0 s1;
  30. }struct_6;
  31. union union_0{
  32. int a;
  33. char b[30];
  34. long int c;
  35. }union_1;
  36. union test_a{
  37. int a;
  38. double b;
  39. };
  40. typedef struct{
  41. int a;
  42. short b;
  43. union union_0 *c;
  44. }struct_7;
  45. typedef struct{
  46. int a;
  47. struct struct_1* b;
  48. }struct_8;
  49. union test{
  50. long int a;
  51. char c[10];
  52. };
  53. typedef struct{
  54. char b[7];
  55. }struct_size7;
  56. typedef struct{
  57. char a[9];
  58. }struct_size9;
  59. union test_3{
  60. struct_size7 c;
  61. struct_size9 d;
  62. };
  63. typedef struct{
  64. int a;
  65. char b[10];
  66. }struct_12;
  67. union test_11{
  68. int a;
  69. char b[10];
  70. };
  71. union test_3 union_7add9;
  72. union test test_1;
  73. union test_a test_2;
  74. union test_11 test_12;
  75. printf("struct_1 size = %d\n",sizeof(struct_1));
  76. printf("struct_2 size = %d\n",sizeof(struct_2));
  77. printf("struct_3 size = %d\n",sizeof(struct_3));
  78. printf("struct_4 size = %d\n",sizeof(struct_4));
  79. printf("struct_5 size = %d\n",sizeof(struct_5));
  80. printf("struct_6 size = %d\n",sizeof(struct_6));
  81. printf("union_1 size = %d\n",sizeof(union_1));
  82. printf("struct_7 size = %d\n",sizeof(struct_7));
  83. printf("struct_8 size = %d\n",sizeof(struct_8));
  84. printf("test_1 size = %d\n",sizeof(test_1));
  85. printf("test_2 size = %d\n",sizeof(test_2));
  86. printf("struct_size7 = %d\n",sizeof(struct_size7));
  87. printf("struct_size9 = %d\n",sizeof(struct_size9));
  88. printf("union_7add9 = %d\n",sizeof(union_7add9));
  89. printf("struct_12 = %d\n",sizeof(struct_12));
  90. printf("test_12 = %d\n",sizeof(test_12));
  91. }

輸出結(jié)果:

  1. struct_1 size = 8
  2. struct_2 size = 16
  3. struct_3 size = 24
  4. struct_4 size = 16
  5. struct_5 size = 8
  6. struct_6 size = 8
  7. union_1  size = 32
  8. struct_7 size = 16
  9. struct_8 size = 16
  10. test_1 size = 16
  11. test_2 size = 8
  12. struct_size7 = 7
  13. struct_size9 = 9
  14. union_7add9 = 9
  15. struct_12 = 16
  16. test_12 = 12


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多