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

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

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

2019-11-09 19:13:38
字體:
供稿:網(wǎng)友

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

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

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

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

  除了變量的自身對齊參數(shù)外,還有一個(gè)對齊參數(shù),就是每個(gè)編譯器默認(rèn)的對齊參數(shù)#PRagma pack(n),這個(gè)值可以通過代碼去設(shè)定,如果沒有設(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條原則了。對于第一條原則,每個(gè)變量相對于結(jié)構(gòu)體的首地址的偏移量必須是對齊參數(shù)的整數(shù)倍,這句話中的對齊參數(shù)是取每個(gè)變量自身對齊參數(shù)和系統(tǒng)默認(rèn)對齊參數(shù)#pragma pack(n)中較小的一個(gè)。舉個(gè)簡單的例子,比如在結(jié)構(gòu)體A中有變量int a,a的自身對齊參數(shù)為4(環(huán)境為windows/vc),而VC默認(rèn)的對齊參數(shù)為8,取較小者,則對于a,它相對于結(jié)構(gòu)體A的起始地址的偏移量必須是4的倍數(shù)。

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

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

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

/*測試sizeof運(yùn)算符 2011.10.1*/#include <iostream>using namespace std;//#pragma pack(4) //設(shè)置4字節(jié)對齊 //#pragma pack() //取消4字節(jié)對齊 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é)分配的情況

比如對于node2

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

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

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

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

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

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

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

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

對于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í)例的存儲地址無關(guān)(注意只有在C++中結(jié)構(gòu)體中才能含有靜態(tài)數(shù)據(jù)成員,而C中結(jié)構(gòu)體中是不允許含有靜態(tài)數(shù)據(jù)成員的)。其在內(nèi)存中存儲方式如下:

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

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

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

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

sizeof(S5)=32。

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

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

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

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

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

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

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

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


發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 宁化县| 喀什市| 吉安市| 鹤山市| 银川市| 广灵县| 綦江县| 望奎县| 安龙县| 正宁县| 收藏| 孟连| 泰安市| 浦北县| 黔西县| 永城市| 西乡县| 吴川市| 城市| 克山县| 沙田区| 阳信县| 龙井市| 新竹市| 桐庐县| 来凤县| 股票| 通州区| 哈巴河县| 砀山县| 定远县| 台南县| 和静县| 昭觉县| 博罗县| 陈巴尔虎旗| 清远市| 景洪市| 成安县| 辉南县| 盐城市|