国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

【轉(zhuǎn)載】結(jié)構(gòu)體字節(jié)對(duì)齊

2019-11-09 19:09:53
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

作者:海子      出處:http://www.cnblogs.com/dolphin0520/      本博客中未標(biāo)明轉(zhuǎn)載的文章歸作者海子和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁(yè)面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。

結(jié)構(gòu)體字節(jié)對(duì)齊 在用sizeof運(yùn)算符求算某結(jié)構(gòu)體所占空間時(shí),并不是簡(jiǎn)單地將結(jié)構(gòu)體中所有元素各自占的空間相加,這里涉及到內(nèi)存字節(jié)對(duì)齊的問(wèn)題。從理論上講,對(duì)于任何變量的訪問(wèn)都可以從任何地址開(kāi)始訪問(wèn),但是事實(shí)上不是如此,實(shí)際上訪問(wèn)特定類型的變量只能在特定的地址訪問(wèn),這就需要各個(gè)變量在空間上按一定的規(guī)則排列,而不是簡(jiǎn)單地順序排列,這就是內(nèi)存對(duì)齊。 內(nèi)存對(duì)齊的原因: 1)某些平臺(tái)只能在特定的地址處訪問(wèn)特定類型的數(shù)據(jù); 2)提高存取數(shù)據(jù)的速度。比如有的平臺(tái)每次都是從偶地址處讀取數(shù)據(jù),對(duì)于一個(gè)int型的變量,若從偶地址單元處存放,則只需一個(gè)讀取周期即可讀取該變量;但是若從奇地址單元處存放,則需要2個(gè)讀取周期讀取該變量。

  在C99標(biāo)準(zhǔn)中,對(duì)于內(nèi)存對(duì)齊的細(xì)節(jié)沒(méi)有作過(guò)多的描述,具體的實(shí)現(xiàn)交由編譯器去處理,所以在不同的編譯環(huán)境下,內(nèi)存對(duì)齊可能略有不同,但是對(duì)齊的最基本原則是一致的,對(duì)于結(jié)構(gòu)體的字節(jié)對(duì)齊主要有下面兩點(diǎn):    1)結(jié)構(gòu)體每個(gè)成員相對(duì)結(jié)構(gòu)體首地址的偏移量(offset)是對(duì)齊參數(shù)的整數(shù)倍,如有需要會(huì)在成員之間填充字節(jié)。編譯器在為結(jié)構(gòu)體成員開(kāi)辟空間時(shí),首先檢查預(yù)開(kāi)辟空間的地址相對(duì)于結(jié)構(gòu)體首地址的偏移量是否為對(duì)齊參數(shù)的整數(shù)倍,若是,則存放該成員;若不是,則填充若干字節(jié),以達(dá)到整數(shù)倍的要求。    2)結(jié)構(gòu)體變量所占空間的大小是對(duì)齊參數(shù)大小的整數(shù)倍。如有需要會(huì)在最后一個(gè)成員末尾填充若干字節(jié)使得所占空間大小是對(duì)齊參數(shù)大小的整數(shù)倍。   注意:在看這兩條原則之前,先了解一下對(duì)齊參數(shù)這個(gè)概念。對(duì)于每個(gè)變量,它自身有對(duì)齊參數(shù),這個(gè)自身對(duì)齊參數(shù)在不同編譯環(huán)境下不同。下面列舉的是兩種最常見(jiàn)的編譯環(huán)境下各種類型變量的自身對(duì)齊參數(shù)

  從上面可以發(fā)現(xiàn),在windows(32)/VC6.0下各種類型的變量的自身對(duì)齊參數(shù)就是該類型變量所占字節(jié)數(shù)的大小,而在linux(32)/GCC下double類型的變量自身對(duì)齊參數(shù)是4,是因?yàn)閘inux(32)/GCC下如果該類型變量的長(zhǎng)度沒(méi)有超過(guò)CPU的字長(zhǎng),則以該類型變量的長(zhǎng)度作為自身對(duì)齊參數(shù),如果該類型變量的長(zhǎng)度超過(guò)CPU字長(zhǎng),則自身對(duì)齊參數(shù)為CPU字長(zhǎng),而32位系統(tǒng)其CPU字長(zhǎng)是4,所以linux(32)/GCC下double類型的變量自身對(duì)齊參數(shù)是4,如果是在Linux(64)下,則double類型的自身對(duì)齊參數(shù)是8。

  除了變量的自身對(duì)齊參數(shù)外,還有一個(gè)對(duì)齊參數(shù),就是每個(gè)編譯器默認(rèn)的對(duì)齊參數(shù)#PRagma pack(n),這個(gè)值可以通過(guò)代碼去設(shè)定,如果沒(méi)有設(shè)定,則取系統(tǒng)的默認(rèn)值。在windows(32)/VC6.0下,n的取值可以為1、2、4、8,默認(rèn)情況下為8。在linux(32)/GCC下,n的取值只能為1、2、4,默認(rèn)情況下為4。注意像DEV-CPP、MinGW等在windows下n的取值和VC的相同。

  了解了這2個(gè)概念之后,可以理解上面2條原則了。對(duì)于第一條原則,每個(gè)變量相對(duì)于結(jié)構(gòu)體的首地址的偏移量必須是對(duì)齊參數(shù)的整數(shù)倍,這句話中的對(duì)齊參數(shù)是取每個(gè)變量自身對(duì)齊參數(shù)和系統(tǒng)默認(rèn)對(duì)齊參數(shù)#pragma pack(n)中較小的一個(gè)。舉個(gè)簡(jiǎn)單的例子,比如在結(jié)構(gòu)體A中有變量int a,a的自身對(duì)齊參數(shù)為4(環(huán)境為windows/vc),而VC默認(rèn)的對(duì)齊參數(shù)為8,取較小者,則對(duì)于a,它相對(duì)于結(jié)構(gòu)體A的起始地址的偏移量必須是4的倍數(shù)。

  對(duì)于第二條原則,結(jié)構(gòu)體變量所占空間的大小是對(duì)齊參數(shù)的整數(shù)倍。這句話中的對(duì)齊參數(shù)有點(diǎn)復(fù)雜,它是取結(jié)構(gòu)體中所有變量的對(duì)齊參數(shù)的最大值和系統(tǒng)默認(rèn)對(duì)齊參數(shù)#pragma pack(n)比較,較小者作為對(duì)齊參數(shù)。舉個(gè)例子假如在結(jié)構(gòu)體A中先后定義了兩個(gè)變量int a;double b;對(duì)于變量a,它的自身對(duì)齊參數(shù)為4,而#pragma pack(n)值默認(rèn)為8,則a的對(duì)齊參數(shù)為4;b的自身對(duì)齊參數(shù)為8,而#pragma pack(n)的默認(rèn)值為8,則b的對(duì)齊參數(shù)為8。由于a的最終對(duì)齊參數(shù)為4,b的最終對(duì)齊參數(shù)為8,那么兩者較大者是8,然后再拿8和#pragma pack(n)作比較,取較小者作為對(duì)齊參數(shù),也就是8,即意味著結(jié)構(gòu)體最終的大小必須能被8整除。

下面是測(cè)試?yán)樱?/p>

注意:以下例子的測(cè)試結(jié)果均在windows(32)/VC下測(cè)試的,其默認(rèn)對(duì)齊參數(shù)為8

/*測(cè)試sizeof運(yùn)算符 2011.10.1*/#include <iostream>using namespace std;//#pragma pack(4) //設(shè)置4字節(jié)對(duì)齊 //#pragma pack() //取消4字節(jié)對(duì)齊 typedef struct node1{ int a; char b; short c;}S1;typedef struct node2{ char a; int b; short c;}S2;typedef struct node3{ int a; short b; static int c;}S3;typedef struct node4{ bool a; S1 s1; short b;}S4;typedef struct node5{ bool a; S1 s1; double b; int c;}S5;int main(int argc, char *argv[]){ cout<<sizeof(char)<<" "<<sizeof(short)<<" "<<sizeof(int)<<" "<<sizeof(float)<<" "<<sizeof(double)<<endl; S1 s1; S2 s2; S3 s3; S4 s4; S5 s5; cout<<sizeof(s1)<<" "<<sizeof(s2)<<" "<<sizeof(s3)<<" "<<sizeof(s4)<<" "<<sizeof(s5)<<endl; return 0;}

下面解釋一下其中的幾個(gè)結(jié)構(gòu)體字節(jié)分配的情況

比如對(duì)于node2

typedef struct node2{ char a; int b; short c;}S2;

sizeof(S2)=12;   對(duì)于變量a,它的自身對(duì)齊參數(shù)為1,#pragma pack(n)默認(rèn)值為8,則最終a的對(duì)齊參數(shù)為1,為其分配1字節(jié)的空間,它相對(duì)于結(jié)構(gòu)體起始地址的偏移量為0,能被4整除;

  對(duì)于變量b,它的自身對(duì)齊參數(shù)為4,#pragma pack(n)默認(rèn)值為8,則最終b的對(duì)齊參數(shù)為4,接下來(lái)的地址相對(duì)于結(jié)構(gòu)體的起始地址的偏移量為1,1不能夠整除4,所以需要在a后面填充3字節(jié)使得偏移量達(dá)到4,然后再為b分配4字節(jié)的空間;

  對(duì)于變量c,它的自身對(duì)齊參數(shù)為2,#pragma pack(n)默認(rèn)值為8,則最終c的對(duì)齊參數(shù)為2,而接下來(lái)的地址相對(duì)于結(jié)構(gòu)體的起始地址的偏移量為8,能整除2,所以直接為c分配2字節(jié)的空間。

  此時(shí)結(jié)構(gòu)體所占的字節(jié)數(shù)為1+3+4+2=10字節(jié)

  最后由于a,b,c的最終對(duì)齊參數(shù)分別為1,4,2,最大為4,#pragma pack(n)的默認(rèn)值為8,則結(jié)構(gòu)體變量最后的大小必須能被4整除。而10不能夠整除4,所以需要在后面填充2字節(jié)達(dá)到12字節(jié)。其存儲(chǔ)如下:

|char|----|----|----| 4字節(jié)|--------int--------| 4字節(jié)|--short--|----|----| 4字節(jié)

  總共占12個(gè)字節(jié)

對(duì)于node3,含有靜態(tài)數(shù)據(jù)成員

typedef struct node3{ int a; short b; static int c;}S3;

  則sizeof(S3)=8.這里結(jié)構(gòu)體中包含靜態(tài)數(shù)據(jù)成員,而靜態(tài)數(shù)據(jù)成員的存放位置與結(jié)構(gòu)體實(shí)例的存儲(chǔ)地址無(wú)關(guān)(注意只有在C++中結(jié)構(gòu)體中才能含有靜態(tài)數(shù)據(jù)成員,而C中結(jié)構(gòu)體中是不允許含有靜態(tài)數(shù)據(jù)成員的)。其在內(nèi)存中存儲(chǔ)方式如下:

  |--------int--------| 4字節(jié)  |--short-|----|----| 4字節(jié)

  而變量c是單獨(dú)存放在靜態(tài)數(shù)據(jù)區(qū)的,因此用siezof計(jì)算其大小時(shí)沒(méi)有將c所占的空間計(jì)算進(jìn)來(lái)。

而對(duì)于node5,里面含有結(jié)構(gòu)體變量

typedef struct node5{ bool a; S1 s1; double b; int c;}S5;

sizeof(S5)=32。

  對(duì)于變量a,其自身對(duì)齊參數(shù)為1,#pragma pack(n)為8,則a的最終對(duì)齊參數(shù)為1,為它分配1字節(jié)的空間,它相對(duì)于結(jié)構(gòu)體起始地址的偏移量為0,能被1整除;

  對(duì)于s1,它的自身對(duì)齊參數(shù)為4(對(duì)于結(jié)構(gòu)體變量,它的自身對(duì)齊參數(shù)為它里面各個(gè)變量最終對(duì)齊參數(shù)的最大值),#pragma pack(n)為8,所以s1的最終對(duì)齊參數(shù)為4,接下來(lái)的地址相對(duì)于結(jié)構(gòu)體起始地址的偏移量為1,不能被4整除,所以需要在a后面填充3字節(jié)達(dá)到4,為其分配8字節(jié)的空間;

  對(duì)于變量b,它的自身對(duì)齊參數(shù)為8,#pragma pack(n)的默認(rèn)值為8,則b的最終對(duì)齊參數(shù)為8,接下來(lái)的地址相對(duì)于結(jié)構(gòu)體起始地址的偏移量為12,不能被8整除,所以需要在s1后面填充4字節(jié)達(dá)到16,再為b分配8字節(jié)的空間;

  對(duì)于變量c,它的自身對(duì)齊參數(shù)為4,#pragma pack(n)的默認(rèn)值為8,則c的最終對(duì)齊參數(shù)為4,接下來(lái)相對(duì)于結(jié)構(gòu)體其實(shí)地址的偏移量為24,能夠被4整除,所以直接為c分配4字節(jié)的空間。

  此時(shí)結(jié)構(gòu)體所占字節(jié)數(shù)為1+3+8+4+8+4=28字節(jié)。

  對(duì)于整個(gè)結(jié)構(gòu)體來(lái)說(shuō),各個(gè)變量的最終對(duì)齊參數(shù)為1,4,8,4,最大值為8,#pragma pack(n)默認(rèn)值為8,所以最終結(jié)構(gòu)體的大小必須是8的倍數(shù),因此需要在最后面填充4字節(jié)達(dá)到32字節(jié)。其存儲(chǔ)如下:

  |--------bool--------| 4字節(jié)   |---------s1---------| 8字節(jié)   |--------------------| 4字節(jié)   |--------double------| 8字節(jié)   |----int----|---------| 8字節(jié)

  另外可以顯示地在程序中使用#pragma pack(n)來(lái)設(shè)置系統(tǒng)默認(rèn)的對(duì)齊參數(shù),在顯示設(shè)置之后,則以設(shè)置的值作為標(biāo)準(zhǔn),其它的和上面所講的類似,就不再贅述了,讀者可以自行上機(jī)試驗(yàn)一下。如果需要取消設(shè)置,可以用#pragma pack()來(lái)取消。


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 色达县| 芦溪县| 天气| 靖安县| 鄢陵县| 漳浦县| 商南县| 华亭县| 云梦县| 共和县| 岱山县| 青冈县| 历史| 阿鲁科尔沁旗| 青岛市| 七台河市| 龙江县| 深泽县| 吴旗县| 沧源| 竹北市| 丁青县| 伊宁市| 阳谷县| 乌什县| 邻水| 亳州市| 陇南市| 宝兴县| 龙江县| 修水县| 鹤庆县| 当涂县| 大新县| 曲水县| 北流市| 蓬莱市| 尼木县| 芜湖市| 通城县| 弥渡县|