摘要:首先感謝三位博主,并做出總結(jié).
首先了解一下struct的儲存結(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。
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)體的大小,這種自引用是合法的。
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;則表示如下圖空間:
對齊規(guī)則
例:(在此平臺上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)存途徑共存
一個例子了然:
所以說,管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呢~
這種邏輯上的錯誤是很難找出來的(只有當數(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):
結(jié)果:
11020304
證明我的32位linux是小端(little-endian)
4.聯(lián)合體union所占內(nèi)存空間大?。?/strong>
前邊說了,首先,union的首地址是固定的,那么,union到底總共有多大?根據(jù)一些小常識,做個不嚴謹不高深的基礎(chǔ)版驗證吧。
根據(jù):分配??臻g的時候內(nèi)存地址基本上是連續(xù)的,至少同類型能保證在一起,連續(xù)就說明,我如果弄三個結(jié)構(gòu)體出來,他們?nèi)齻€地址應(yīng)該連著,看一下三個地址的間隔就知道了。
打印,可以看到結(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ù)組,其他不變:
此時sizeTest大小為16字節(jié),因為需要當數(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的成員變量是相當于開辟了幾個接口(即union包含的變量)!但是,沒開辟就不能用了?當然也能用!
寫個小測試:
一個例子了然,我的結(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)體的自引用(self reference),就是在結(jié)構(gòu)體內(nèi)部,包含指向自身類型結(jié)構(gòu)體的指針。
結(jié)構(gòu)體的相互引用(mutual reference),就是說在多個結(jié)構(gòu)體中,都包含指向其他結(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)部是不能使用的,因為還沒定義。
正確的方式:有三種,差別不大,使用哪種都可以。
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;
錯誤的方式:
typedef struct tag_a{ int value; B *bp; } A; typedef struct tag_b{ int value; A *ap; } B;
錯誤的原因和上面一樣,這里類型B在定義之前 就被使用。
正確的方式:(使用“不完全聲明”)
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; };
嵌套結(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)存大小.
- #include <stdio.h>
- void main()
- {
- typedef struct{
- int a;
- short b;
- }struct_1;
- typedef struct_1* struct_0;
- typedef struct{
- int a;
- char b;
- struct_1 s1;
- }struct_2;
- typedef struct{
- int a;
- char b;
- long int c;
- struct_1 s1;
- }struct_3;
- typedef struct{
- int a;
- char b;
- struct_1 * s1;
- }struct_4;
- typedef struct{
- struct_1 * s1;
- }struct_5;
- typedef struct{
- struct_0 s1;
- }struct_6;
- union union_0{
- int a;
- char b[30];
- long int c;
- }union_1;
- union test_a{
- int a;
- double b;
- };
- typedef struct{
- int a;
- short b;
- union union_0 *c;
- }struct_7;
- typedef struct{
- int a;
- struct struct_1* b;
- }struct_8;
- union test{
- long int a;
- char c[10];
- };
- typedef struct{
- char b[7];
- }struct_size7;
- typedef struct{
- char a[9];
- }struct_size9;
- union test_3{
- struct_size7 c;
- struct_size9 d;
- };
- typedef struct{
- int a;
- char b[10];
- }struct_12;
- union test_11{
- int a;
- char b[10];
- };
- union test_3 union_7add9;
- union test test_1;
- union test_a test_2;
- union test_11 test_12;
- printf("struct_1 size = %d\n",sizeof(struct_1));
- printf("struct_2 size = %d\n",sizeof(struct_2));
- printf("struct_3 size = %d\n",sizeof(struct_3));
- printf("struct_4 size = %d\n",sizeof(struct_4));
- printf("struct_5 size = %d\n",sizeof(struct_5));
- printf("struct_6 size = %d\n",sizeof(struct_6));
- printf("union_1 size = %d\n",sizeof(union_1));
- printf("struct_7 size = %d\n",sizeof(struct_7));
- printf("struct_8 size = %d\n",sizeof(struct_8));
- printf("test_1 size = %d\n",sizeof(test_1));
- printf("test_2 size = %d\n",sizeof(test_2));
- printf("struct_size7 = %d\n",sizeof(struct_size7));
- printf("struct_size9 = %d\n",sizeof(struct_size9));
- printf("union_7add9 = %d\n",sizeof(union_7add9));
- printf("struct_12 = %d\n",sizeof(struct_12));
- printf("test_12 = %d\n",sizeof(test_12));
- }
輸出結(jié)果:
- struct_1 size = 8
- struct_2 size = 16
- struct_3 size = 24
- struct_4 size = 16
- struct_5 size = 8
- struct_6 size = 8
- union_1 size = 32
- struct_7 size = 16
- struct_8 size = 16
- test_1 size = 16
- test_2 size = 8
- struct_size7 = 7
- struct_size9 = 9
- union_7add9 = 9
- struct_12 = 16
- test_12 = 12