摘要:首先感謝三位博主,并做出總結(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)存途徑共存 一個例子了然:
所以說,管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):
不過話說回來,某些情況下雖然不是很節(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)然也能用! 寫個小測試:
一個例子了然,我的結(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)部是不能使用的,因為還沒定義。 正確的方式:有三種,差別不大,使用哪種都可以。 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;
2. 相互引用 結(jié)構(gòu)體錯誤的方式: 錯誤的原因和上面一樣,這里類型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)體的自引用中,如下這種情況是非法的 但很多時候,的確需要使用到自引用,有個技巧,如下: 這里有一種情況值得注意: 以上轉(zhuǎn)自:http://www.cnblogs.com/renyuan/archive/2012/11/30/2796792.html. 下面有個測試小程序:(基本上涵蓋了所有的情況)幫助你更好的理解結(jié)構(gòu)體和聯(lián)合體占用的內(nèi)存大小.
輸出結(jié)果:
|
|