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