結(jié)構(gòu)體字節(jié)對(duì)齊C/C++ 2009-11-06 12:37:39 閱讀136 評(píng)論0 字號(hào):大中小 訂閱 http://hi.baidu.com/skyland_lee/blog/item/45604bca81e1928cc91768d7.html,這篇文章比較專業(yè)地分析了字節(jié)對(duì)齊在VC和GCC的特點(diǎn),寫得非常不錯(cuò)。這兩天我在為畢業(yè)設(shè)計(jì)做準(zhǔn)備,所以要花點(diǎn)時(shí)間研究字節(jié)對(duì)齊的問(wèn)題。
文章中有這樣一段話:
Win32平臺(tái)下的微軟C編譯器(cl.exe for 80×86)的對(duì)齊策略:
1) 結(jié)構(gòu)體變量的首地址能夠被其最寬基本類型成員的大小所整除;
備注:編譯器在給結(jié)構(gòu)體開辟空間時(shí),首先找到結(jié)構(gòu)體中最寬的基本數(shù)據(jù)類型,然后尋找內(nèi)存地址能被該基本數(shù)據(jù)類型所整除的位置,作為結(jié)構(gòu)體的首地址。將這個(gè)最寬的基本數(shù)據(jù)類型的大小作為上面介紹的對(duì)齊模數(shù)。
2) 結(jié)構(gòu)體每個(gè)成員相對(duì)于結(jié)構(gòu)體首地址的偏移量(offset)都是成員大小的整數(shù)倍,如有需要編譯器會(huì)在成員之間加上填充字節(jié)(internal adding);
備注:為結(jié)構(gòu)體的一個(gè)成員開辟空間之前,編譯器首先檢查預(yù)開辟空間的首地址相對(duì)于結(jié)構(gòu)體首地址的偏移是否是本成員的整數(shù)倍,若是,則存放本成員,反之,則在本成員和上一個(gè)成員之間填充一定的字節(jié),以達(dá)到整數(shù)倍的要求,也就是將預(yù)開辟空間的首地址后移幾個(gè)字節(jié)。
3) 結(jié)構(gòu)體的總大小為結(jié)構(gòu)體最寬基本類型成員大小的整數(shù)倍,如有需要,編譯器會(huì)在最末一個(gè)成員之后加上填充字節(jié)(trailing padding)。
備注:結(jié)構(gòu)體總大小是包括填充字節(jié),最后一個(gè)成員滿足上面兩條以外,還必須滿足第三條,否則就必須在最后填充幾個(gè)字節(jié)以達(dá)到本條要求。
通過(guò)一個(gè)例子來(lái)驗(yàn)證之,有這樣一個(gè)結(jié)構(gòu)體:
struct S
{
int a;
double b;
int c;
};
sizeof得到它的大小為24,顯然為了滿足第2)條,在a后面填充了4個(gè)字節(jié),以保證b成員的地址相對(duì)于結(jié)構(gòu)體首地址的偏移量是8的倍數(shù);為了滿足3)條,在c的后面填充了4個(gè)字節(jié),使得結(jié)構(gòu)體的總大小為結(jié)構(gòu)體最寬基本類型成員大小的整數(shù)倍。這樣sizeof(S)就是24了。至于第一條,我倒是第一次看到,驗(yàn)證了下發(fā)現(xiàn)有點(diǎn)奇怪,測(cè)試代碼如下(VS2008):
struct S s1;
int a = 4;
struct S s2;
printf("%d, %d\n", &s1, (int)(&s1) % 8);
printf("%d, %d\n", &s2, (int)(&s2) % 8);
觀察內(nèi)存可知,&s1是能被8整除的,s1后面填充了好多字節(jié),然后就是a,a后面又填充了一些字節(jié),然后才是s2,感覺(jué)有點(diǎn)亂?最后發(fā)現(xiàn)"(int)&s2 % 8"等于4,這就不符合1)條了。
如果使用“#pragma pack(4)聲明下”,這個(gè)結(jié)構(gòu)體的大小就會(huì)變成16,也就是按照4字節(jié)對(duì)齊了。pack(n)用于指明對(duì)齊的最大值。
文章中對(duì)于位域結(jié)構(gòu)體有這樣一段話:
如果結(jié)構(gòu)體中含有位域(bit-field),那么VC中準(zhǔn)則又要有所更改:
1) 如果相鄰位域字段的類型相同,且其位寬之和小于類型的sizeof大小,則后面的字段將緊鄰前一個(gè)字段存儲(chǔ),直到不能容納為止;
2) 如果相鄰位域字段的類型相同,但其位寬之和大于類型的sizeof大小,則后面的字段將從新的存儲(chǔ)單元開始,其偏移量為其類型大小的整數(shù)倍;
3) 如果相鄰的位域字段的類型不同,則各編譯器的具體實(shí)現(xiàn)有差異,VC6采取不壓縮方式(不同位域字段存放在不同的位域類型字節(jié)中),Dev-C++和GCC都采取壓縮方式;
用幾個(gè)例子來(lái)測(cè)試一下:
struct S2
{
int a: 2;
int b: 3;
int c: 3;
};
struct S3
{
char a : 2;
int b : 3;
};
sizeof(S2)=4,滿足1)條;sizeof(S3)=8,滿足3)條。
同GCC的差異性。GCC最大的對(duì)齊模數(shù)為4,所以S的大小為16;S2,S3的大小為4,因?yàn)镚CC會(huì)將S3中的a和b壓縮在一個(gè)4字節(jié)中,VC卻采用非壓縮方式。
|
|
來(lái)自: chensirDSP > 《我的圖書館》