摘要:首先感謝三位博主,并做出總結(jié). 首先了解一下struct的儲(chǔ)存結(jié)構(gòu): 一、結(jié)構(gòu)體的訪問(wèn)1.結(jié)構(gòu)體成員的的直接訪問(wèn),如下結(jié)構(gòu)體:struct A{ int a; long *b; char c[20]; }; struct A com; 結(jié)構(gòu)體成員通過(guò)操作符"."訪問(wèn),表達(dá)式com.a的結(jié)果是個(gè)數(shù)組名,可以把它使用在任何可以使用數(shù)組名的地方,com.a[4],將選擇一個(gè)數(shù)組元素。 2、結(jié)構(gòu)體成員的間接訪問(wèn) struct A *p; 可以使用(*p).a訪問(wèn)結(jié)構(gòu)體成員,但這種形式有點(diǎn)不簡(jiǎn)潔所以使用操作符"->"來(lái)訪問(wèn)結(jié)構(gòu)體成員,箭頭操作符對(duì)左操作數(shù)執(zhí)行間接訪問(wèn)來(lái)獲取指針?biāo)赶虻慕Y(jié)構(gòu),然后根據(jù)右操作數(shù)來(lái)訪問(wèn)一個(gè)成員,p->a。 二、結(jié)構(gòu)體的自引用struct B{ int a; struct B b; int c; }; 這種引用是不合法的,因?yàn)閎是一個(gè)完整的結(jié)構(gòu),第二個(gè)成員又是另一個(gè)完整的結(jié)構(gòu),還包括她自己的成員,這樣會(huì)循環(huán)下去無(wú)法及所結(jié)構(gòu)體的大小。 struct B{ int a; struct B *b; int c; }; 這種聲明是合法的,b現(xiàn)在是一個(gè)指針?biāo)嫉淖止?jié)數(shù)是已知的,可以計(jì)算出結(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; 如下圖來(lái)表示此結(jié)構(gòu): 創(chuàng)建一個(gè)指向整型的指針:int *p1;若要使它指向整型成員a,應(yīng)使用&取得一個(gè)指向p->a的指針:p1=&p->a. 訪問(wèn)嵌套的結(jié)構(gòu)體:p->c.a即為訪問(wèn)c結(jié)構(gòu)體中的整形a。 訪問(wèn)指針成員:定義另一結(jié)構(gòu):Ex y; x.d=&y;則Ex->d->c.b[1]=4;則表示如下圖空間: 四、結(jié)構(gòu)體的內(nèi)存對(duì)齊對(duì)齊規(guī)則
每個(gè)特定平臺(tái)上的編譯器都有自己的默認(rèn)“對(duì)齊系數(shù)”,也可以通過(guò)預(yù)編譯命令#pragma pack(n),n=1,2,4,8,16來(lái)改變這一系數(shù),其中的n就是你要指定的“對(duì)齊系數(shù)”。
規(guī)則:
1、數(shù)據(jù)成員對(duì)齊規(guī)則:struct數(shù)據(jù)成員,第一個(gè)數(shù)據(jù)成員放在offset為0的地方,以后每個(gè)數(shù)據(jù)成員的對(duì)齊按照#pragma pack指定的數(shù)值和這個(gè)數(shù)據(jù)成員自身長(zhǎng)度中,比較小的那個(gè)進(jìn)行。(即第一個(gè)數(shù)據(jù)成員以后的成員的偏移地址為#pragma pack指定的數(shù)值和這個(gè)數(shù)據(jù)成員自身長(zhǎng)度中,比較小的那個(gè)的整數(shù)倍)
2、struct的整體對(duì)齊規(guī)則:在數(shù)據(jù)成員完成各自對(duì)齊之后,結(jié)構(gòu)體本身也要進(jìn)行對(duì)齊,對(duì)齊將按照#pragma pack指定的數(shù)值和struct最大數(shù)據(jù)成員長(zhǎng)度中,比較小的那個(gè)進(jìn)行。
3、當(dāng)#pragma pack的n值等于或超過(guò)所有數(shù)據(jù)成員長(zhǎng)度的時(shí)候,這個(gè)n值的大小將不產(chǎn)生任何效果。
當(dāng)結(jié)構(gòu)體里面包含另一結(jié)構(gòu)體時(shí),直接將該結(jié)構(gòu)體中的內(nèi)容代換進(jìn)去,計(jì)算其總的存儲(chǔ)空間即可。
例:(在此平臺(tái)上int占4個(gè)字節(jié)) 雖然A和A1所包含的成員相同,但A占了12個(gè)字節(jié),A1占8個(gè)字節(jié),所以在聲明中對(duì)結(jié)構(gòu)體列表的排列,因該讓邊界要求嚴(yán)格的成員首先出現(xiàn)(數(shù)據(jù)成員自生長(zhǎng)度大的先出現(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ù)類型和變量。 不過(guò)區(qū)別也挺明顯: 結(jié)構(gòu)體(struct)中所有變量是“共存”的——優(yōu)點(diǎn)是“有容乃大”,全面;缺點(diǎn)是struct內(nèi)存空間的分配是粗放的,不管用不用,全分配。 而聯(lián)合體(union)中是各變量是“互斥”的——缺點(diǎn)就是不夠“包容”;但優(yōu)點(diǎn)是內(nèi)存使用更為精細(xì)靈活,也節(jié)省了內(nèi)存空間。
2.雙刃劍——多種訪問(wèn)內(nèi)存途徑共存 一個(gè)例子了然:
所以說(shuō),管union的叫共用體還真是貼切——完全就是共用一個(gè)內(nèi)存首地址,并且各種變量名都可以同時(shí)使用,操作也是共同生效。如此多的access內(nèi)存手段,確實(shí)好用,不過(guò)這些“手段”之間卻沒(méi)法互相屏蔽——就好像數(shù)組+下標(biāo)和指針+偏移一樣。 上例中我改了v.i的值,結(jié)果v.l也能讀取,那么也許我還以為v.l是我想要的值呢,因?yàn)樯线吿岬搅藆nion的內(nèi)存首地址肯定是相同的,那么還有一種情況和上邊類似: 一個(gè)int數(shù)組變量a,一個(gè)long int(32位機(jī)中,long int占4字節(jié),與int相同)變量b,我即使沒(méi)給int變量b賦值,因?yàn)閿?shù)據(jù)類型相同,我使用int變量b也完全會(huì)拿出int數(shù)組a中的a[0]來(lái),一些時(shí)候一不小心用上,還以為用的就是變量b呢~ 這種邏輯上的錯(cuò)誤是很難找出來(lái)的(只有當(dāng)數(shù)據(jù)類型相去甚遠(yuǎn)的時(shí)候稍好,出個(gè)亂碼什么的很容易發(fā)現(xiàn)錯(cuò)誤)。
PS:感謝熱心網(wǎng)友的提醒“在union定義結(jié)束時(shí)加分號(hào)”,其實(shí)是可以不加的,因?yàn)樗辉谥骱瘮?shù)內(nèi),不是執(zhí)行的語(yǔ)句,如果是主函數(shù)內(nèi)聲明的union就必須加分號(hào)了,在主函數(shù)內(nèi)不加分號(hào)就涉及到基礎(chǔ)常識(shí)了——沒(méi)有分號(hào)隔開(kāi)怎能叫一句。
3.聯(lián)合體union和大小端(big-endian、little-endian):
不過(guò)話說(shuō)回來(lái),某些情況下雖然不是很節(jié)約內(nèi)存空間,但是union的復(fù)用性優(yōu)勢(shì)依然存在啊,比如方便多命名,這種“二義性”,從某些方面也可能是優(yōu)勢(shì)。這種方法還有個(gè)好處,就是某些寄存器或通道大小有限制的情況下,可以分多次搬運(yùn)。
6.本質(zhì)&進(jìn)階:
根據(jù)union固定首地址和union按最大需求開(kāi)辟一段內(nèi)存空間兩個(gè)特征,可以發(fā)現(xiàn),所有表面的定義都是虛的,所謂聯(lián)合體union,就是在內(nèi)存給你劃了一個(gè)足夠用的空間,至于你怎么玩~它不管~?。ê沃故莡nion和struct,C不就是玩地址么,所以使用C靈活,也容易犯錯(cuò))
沒(méi)錯(cuò),union的成員變量是相當(dāng)于開(kāi)辟了幾個(gè)接口(即union包含的變量)!但是,沒(méi)開(kāi)辟就不能用了?當(dāng)然也能用! 寫個(gè)小測(cè)試:
一個(gè)例子了然,我的結(jié)構(gòu)體只定義了int和double“接口”,只要我獲得地址,往里邊扔什么數(shù)據(jù)誰(shuí)管得到?這就是C語(yǔ)言的強(qiáng)大,這就是union的本質(zhì)——只管開(kāi)辟一段空間。 有些東西,熟悉編譯原理和編譯器工作過(guò)程的話,解決會(huì)更容易點(diǎn),雖然我現(xiàn)在這方面技能不太強(qiáng),不過(guò)一般問(wèn)題也足夠分析了。 以上轉(zhuǎn)自:https://www.cnblogs.com/tianlangshu/p/5204521.html. 結(jié)構(gòu)體的嵌套問(wèn)題結(jié)構(gòu)體的自引用(self reference),就是在結(jié)構(gòu)體內(nèi)部,包含指向自身類型結(jié)構(gòu)體的指針。 結(jié)構(gòu)體的相互引用(mutual reference),就是說(shuō)在多個(gè)結(jié)構(gòu)體中,都包含指向其他結(jié)構(gòu)體的指針。 1. 自引用結(jié)構(gòu)體1.1 不使用typedef時(shí) 錯(cuò)誤的方式: struct tag_1{ struct tag_1 A; int value; }; 這種聲明是錯(cuò)誤的,因?yàn)檫@種聲明實(shí)際上是一個(gè)無(wú)限循環(huán),成員A是一個(gè)結(jié)構(gòu)體,A的內(nèi)部還會(huì)有成員是結(jié)構(gòu)體,依次下去,無(wú)線循環(huán)。在分配內(nèi)存的時(shí)候,由于無(wú)限嵌套,也無(wú)法確定這個(gè)結(jié)構(gòu)體的長(zhǎng)度,所以這種方式是非法的。 正確的方式: (使用指針) struct tag_1{ struct tag_1 *A; int value; }; 由于指針的長(zhǎng)度是確定的(在32位機(jī)器上指針長(zhǎng)度為4),所以編譯器能夠確定該結(jié)構(gòu)體的長(zhǎng)度。 1.2 使用typedef 時(shí) 錯(cuò)誤的方式: typedef struct { int value; NODE *link; } NODE; 這里的目的是使用typedef為結(jié)構(gòu)體創(chuàng)建一個(gè)別名NODEP。但是這里是錯(cuò)誤的,因?yàn)轭愋兔淖饔糜蚴菑恼Z(yǔ)句的結(jié)尾開(kāi)始,而在結(jié)構(gòu)體內(nèi)部是不能使用的,因?yàn)檫€沒(mé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)體錯(cuò)誤的方式: 錯(cuò)誤的原因和上面一樣,這里類型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)體時(shí)應(yīng)注意: 結(jié)構(gòu)體的自引用中,如下這種情況是非法的 但很多時(shí)候,的確需要使用到自引用,有個(gè)技巧,如下: 這里有一種情況值得注意: 以上轉(zhuǎn)自:http://www.cnblogs.com/renyuan/archive/2012/11/30/2796792.html. 下面有個(gè)測(cè)試小程序:(基本上涵蓋了所有的情況)幫助你更好的理解結(jié)構(gòu)體和聯(lián)合體占用的內(nèi)存大小.
輸出結(jié)果:
|
|