最近對結(jié)構(gòu)體對齊比較感興趣,收集了一些資料
結(jié)構(gòu)體對齊的具體含義(#pragma pack)
#pragma pack (4) class TestB { public: int aa; char a; short b; char c; }; int nSize = sizeof(TestB); 這里nSize結(jié)果為12,在預(yù)料之中。
現(xiàn)在去掉第一個成員變量為如下代碼: #pragma pack (4) class TestC { public: char a; short b; char c; }; int nSize = sizeof(TestC); 按照正常的填充方式nSize的結(jié)果應(yīng)該是8,為什么結(jié)果顯示nSize為6呢?
事實上,很多人對#pragma pack 的理解是錯誤的。 #pragma pack 規(guī)定的對齊長度,實際使用的規(guī)則是: 結(jié)構(gòu),聯(lián)合,或者類的數(shù)據(jù)成員,第一個放在偏移為0的地方,以后每個數(shù)據(jù)成員的對齊,按照#pragma pack 指定的數(shù)值和這個數(shù)據(jù)成員自身長度中,比較小的那個進行。 也就是說,當(dāng)#pragma pack 的值等于或超過所有數(shù)據(jù)成員長度的時候,這個值的大小將不產(chǎn)生任何效果。 而結(jié)構(gòu)整體的對齊,則按照結(jié)構(gòu)體中最大的數(shù)據(jù)成員 和 #pragma pack 指定值 之間,較小的那個進行。
具體解釋 #pragma pack (4) class TestB { public: int aa; //第一個成員,放在[0,3]偏移的位置, char a; //第二個成員,自身長為1,#pragma pack (4),取小值,也就是1,所以這個成員按一字節(jié)對齊,放在偏移[4]的位置。 short b; //第三個成員,自身長2,#pragma pack (4),取2,按2字節(jié)對齊,所以放在偏移[6,7]的位置。 char c; //第四個,自身長為1,放在[8]的位置。 }; 這個類實際占據(jù)的內(nèi)存空間是9字節(jié) 類之間的對齊,是按照類內(nèi)部最大的成員的長度,和#pragma pack 規(guī)定的值之中較小的一個對齊的。 所以這個例子中,類之間對齊的長度是min(sizeof(int),4),也就是4。 9按照4字節(jié)圓整的結(jié)果是12,所以sizeof(TestB)是12。
如果 #pragma pack (2) class TestB { public: int aa; //第一個成員,放在[0,3]偏移的位置, char a; //第二個成員,自身長為1,#pragma pack (4),取小值,也就是1,所以這個成員按一字節(jié)對齊,放在偏移[4]的位置。 short b; //第三個成員,自身長2,#pragma pack (4),取2,按2字節(jié)對齊,所以放在偏移[6,7]的位置。 char c; //第四個,自身長為1,放在[8]的位置。 }; //可以看出,上面的位置完全沒有變化,只是類之間改為按2字節(jié)對齊,9按2圓整的結(jié)果是10。 //所以 sizeof(TestB)是10。
最后看原貼: 現(xiàn)在去掉第一個成員變量為如下代碼: #pragma pack (4) class TestC { public: char a;//第一個成員,放在[0]偏移的位置, short b;//第二個成員,自身長2,#pragma pack (4),取2,按2字節(jié)對齊,所以放在偏移[2,3]的位置。 char c;//第三個,自身長為1,放在[4]的位置。 }; //整個類的大小是5字節(jié),按照min(sizeof(short),4)字節(jié)對齊,也就是2字節(jié)對齊,結(jié)果是6 //所以sizeof(TestC)是6。
對於位域有如下規(guī)定:
C99規(guī)定int、unsigned int和bool可以作為位域類型,但編譯器幾乎都對此作了擴展,允許其它類型類型的存在。使用位域的主要目的是壓縮存儲,其大致規(guī)則為: 1) 如果相鄰位域字段的類型相同,且其位寬之和小于類型的sizeof大小,則后面的字段將緊鄰前一個字段存儲,直到不能容納為止; 2) 如果相鄰位域字段的類型相同,但其位寬之和大于類型的sizeof大小,則后面的字段將從新的存儲單元開始,其偏移量為其類型大小的整數(shù)倍; 3) 如果相鄰的位域字段的類型不同,則各編譯器的具體實現(xiàn)有差異,VC6采取不壓縮方式,Dev-C++采取壓縮方式; 4) 如果位域字段之間穿插著非位域字段,則不進行壓縮; 5) 整個結(jié)構(gòu)體的總大小為最寬基本類型成員大小的整數(shù)倍。
還是讓我們來看看例子。 示例1: struct BF1 { char f1 : 3; char f2 : 4; char f3 : 5; }; 其內(nèi)存布局為: |_f1__|__f2__|_|____f3___|____| |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_| 0 3 7 8 1316 位域類型為char,第1個字節(jié)僅能容納下f1和f2,所以f2被壓縮到第1個字節(jié)中,而f3只 能從下一個字節(jié)開始。因此sizeof(BF1)的結(jié)果為2。
示例2: struct BF2 { char f1 : 3; short f2 : 4; char f3 : 5; }; 由于相鄰位域類型不同,在VC6中其sizeof為6,在Dev-C++中為2。
示例3: struct BF3 { char f1 : 3; char f2; char f3 : 5; }; 非位域字段穿插在其中,不會產(chǎn)生壓縮,在VC6和Dev-C++中得到的大小均為3。
|