C99規定int、unsigned int和bool可以作為位域類型。但編譯器幾乎都對此作了擴充,允許其它類型類型的存在。
如果結構體中含有位域(bit-field),總結規則如下:(以下代碼在x86 32bit系統上測試,gcc 4.1.2)
1) 如果相鄰位域欄位的類型相同,且其位寬之和小於類型的sizeof大小,則後面的欄位將緊鄰前一個欄位儲存,直到不能容納為止
例:
struct test1 {
char a : 2;
char b : 3;
char c : 3;
};
3個變數正好佔1個位元組,所以整體只要佔1個位元組
2) 如果相鄰位域欄位的類型相同,但其位寬之和大於類型的sizeof大小,則後面的欄位將從新的儲存單元開始,其位移量為其類型大小的整數倍
例:
struct test2 {
char a : 2;
char b : 3;
char c : 7;
};
3個變數超過1個位元組,a和b在佔一個位元組,c佔一個位元組,所以整體佔2個位元組。
3) 如果相鄰的位域欄位的類型不同,則各編譯器的具體實現有差異,VC6採取不壓縮方式(不同位域欄位存放在不同的位域類型位元組中),Dev-C++和GCC都採取壓縮方式
例1:
struct test3
{
char a:1;
char b:1;
long c:1;
char d:2;
};
4個變數<=1個位元組,所以整體佔1個位元組,對吧!
錯了,開始我也是這樣想的,結果是4個。
我猜測了一下:
雖然a,b,c,d加起來只佔1個位元組,但是最後結構體整體也要進行對齊。
詳細可以查看之前寫的不含位域的記憶體對齊說明http://blog.csdn.net/todd911/article/details/6528428,
對齊規則的第二條:結構(或聯合)的整體對齊規則:在 資料成員完成各自對齊之後,結構(或聯合)本身也要進行對齊,對齊將按照#pragma pack指定的數值和結構(或聯合)最大資料成員長度中,比較小的那個進行。
這邊因為沒有定義#pragma pack,所以預設按4個位元組進行對齊,結構體中最大的數是long,佔4個位元組,所以最終要按4個位元組進行整體對齊。
我們來驗證一下,如果將結構體改為:
struct test3
{
char a:1;
char b:1;
short c:1;
char d:2;
};
結果應該是2。
試一下,確實是2.
再來修改一下:
struct test3
{
char a:1;
char b:1;
long c:31;
char d:2;
};
上面的結構體佔12個位元組。
a,b合起來佔1個位元組,c如果要合到a,b中去,的話就是33bit,超過將按照#pragma pack指定的數值和結構(或聯合)最大資料成員長度中,比較小的那個,
所以c只能自立門戶,從第5個位元組(位移量為32bit)開始放置佔4個位元組,同理最後d也合不進c中去,所以從第9個位元組開始放置,最後結構體整體對齊,結果是12.
總結gcc的壓縮方式:
a.先要獲得#pragma pack指定的數值和結構(或聯合)最大資料成員長度中,比較小的那個值,這邊稱為對齊基數。
b.按照結構體中變數的順序,如果能合在一起且不超過a中的對齊基數的就合并,不能合并的需要自立門戶,位移量為對齊基數的整數倍,繼續檢查是否能進行合并。。。
c.最後結構體整體進行對齊。
4)如果位域欄位之間穿插著非位域欄位,則不進行壓縮,按照不包含位域的結構體進行對齊,詳細規則可以參考我以前寫的文章:http://blog.csdn.net/todd911/article/details/6528428
例:
struct test4
{
char a:1;
char b:1;
short c;
char d:1;
};
佔用6個位元組。