C++ 結(jié)構(gòu)體所占字節(jié)數(shù)

在用sizeof運(yùn)算符求算某結(jié)構(gòu)體所占空間時(shí)留搔,并不是簡(jiǎn)單地將結(jié)構(gòu)體中所有元素各自占的空間相加,這里涉及到內(nèi)存字節(jié)對(duì)齊的問(wèn)題。從理論上講以现,對(duì)于任何變量的訪問(wèn)都可以從任何地址開(kāi)始訪問(wèn),但是事實(shí)上不是如此约啊,實(shí)際上訪問(wèn)特定類型的變量只能在特定的地址訪問(wèn)邑遏,這就需要各個(gè)變量在空間上按一定的規(guī)則排列,而不是簡(jiǎn)單地順序排列恰矩,這就是內(nèi)存對(duì)齊记盒。

????? 內(nèi)存對(duì)齊的原因:

1)某些平臺(tái)只能在特定的地址處訪問(wèn)特定類型的數(shù)據(jù);

? ? ? 2)提高存取數(shù)據(jù)的速度外傅。比如有的平臺(tái)每次都是從偶地址處讀取數(shù)據(jù)纪吮,對(duì)于一個(gè)int型的變量,若從偶地址單元處存放萎胰,則只需一個(gè)讀取周期即可讀取該變量碾盟;但是若從奇地址單元處存放,則需要2個(gè)讀取周期讀取該變量奥洼。

在C99標(biāo)準(zhǔn)中巷疼,對(duì)于內(nèi)存對(duì)齊的細(xì)節(jié)沒(méi)有作過(guò)多的描述,具體的實(shí)現(xiàn)交由編譯器去處理,所以在不同的編譯環(huán)境下嚼沿,內(nèi)存對(duì)齊可能略有不同估盘,但是對(duì)齊的最基本原則是一致的,對(duì)于結(jié)構(gòu)體的字節(jié)對(duì)齊主要有下面兩點(diǎn):

1)結(jié)構(gòu)體每個(gè)成員相對(duì)結(jié)構(gòu)體首地址的偏移量(offset)是對(duì)齊參數(shù)的整數(shù)倍骡尽,如有需要會(huì)在成員之間填充字節(jié)遣妥。編譯器在為結(jié)構(gòu)體成員開(kāi)辟空間時(shí),首先檢查預(yù)開(kāi)辟空間的地址相對(duì)于結(jié)構(gòu)體首地址的偏移量是否為對(duì)齊參數(shù)的整數(shù)倍攀细,若是箫踩,則存放該成員;若不是谭贪,則填充若干字節(jié)境钟,以達(dá)到整數(shù)倍的要求。

2)結(jié)構(gòu)體變量所占空間的大小是對(duì)齊參數(shù)大小的整數(shù)倍俭识。如有需要會(huì)在最后一個(gè)成員末尾填充若干字節(jié)使得所占空間大小是對(duì)齊參數(shù)大小的整數(shù)倍慨削。

注意:在看這兩條原則之前,先了解一下對(duì)齊參數(shù)這個(gè)概念套媚。對(duì)于每個(gè)變量缚态,它自身有對(duì)齊參數(shù),這個(gè)自身對(duì)齊參數(shù)在不同編譯環(huán)境下不同堤瘤。下面列舉的是兩種最常見(jiàn)的編譯環(huán)境下各種類型變量的自身對(duì)齊參數(shù)

從上面可以發(fā)現(xiàn)玫芦,在windows(32)/VC6.0下各種類型的變量的自身對(duì)齊參數(shù)就是該類型變量所占字節(jié)數(shù)的大小,而在linux(32)/GCC下double類型的變量自身對(duì)齊參數(shù)是4本辐,是因?yàn)閘inux(32)/GCC下如果該類型變量的長(zhǎng)度沒(méi)有超過(guò)CPU的字長(zhǎng)桥帆,則以該類型變量的長(zhǎng)度作為自身對(duì)齊參數(shù),如果該類型變量的長(zhǎng)度超過(guò)CPU字長(zhǎng)师郑,則自身對(duì)齊參數(shù)為CPU字長(zhǎng)环葵,而32位系統(tǒng)其CPU字長(zhǎng)是4,所以linux(32)/GCC下double類型的變量自身對(duì)齊參數(shù)是4宝冕,如果是在Linux(64)下张遭,則double類型的自身對(duì)齊參數(shù)是8。

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

對(duì)于第二條原則品洛,結(jié)構(gòu)體變量所占空間的大小是對(duì)齊參數(shù)的整數(shù)倍。這句話中的對(duì)齊參數(shù)有點(diǎn)復(fù)雜摩桶,它是取結(jié)構(gòu)體中所有變量的對(duì)齊參數(shù)的最大值和系統(tǒng)默認(rèn)對(duì)齊參數(shù)#pragma pack(n)比較桥状,較小者作為對(duì)齊參數(shù)。舉個(gè)例子假如在結(jié)構(gòu)體A中先后定義了兩個(gè)變量int a;double b;對(duì)于變量a硝清,它的自身對(duì)齊參數(shù)為4辅斟,而#pragma pack(n)值默認(rèn)為8,則a的對(duì)齊參數(shù)為4芦拿;b的自身對(duì)齊參數(shù)為8士飒,而#pragma pack(n)的默認(rèn)值為8,則b的對(duì)齊參數(shù)為8蔗崎。即結(jié)構(gòu)體內(nèi)的每個(gè)遍歷也要取自身的對(duì)齊參數(shù)和默認(rèn)對(duì)齊參數(shù)比較酵幕,取較小者作為這個(gè)變量的對(duì)齊參數(shù)。由于a的最終對(duì)齊參數(shù)為4缓苛,b的最終對(duì)齊參數(shù)為8芳撒,那么兩者較大者是8,然后再拿8和#pragma pack(n)作比較,取較小者作為對(duì)齊參數(shù)笔刹,也就是8芥备,即意味著結(jié)構(gòu)體最終的大小必須能被8整除。

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

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


#include?<iostream>??

using?namespace?std;??

//#pragma?pack(4)????//設(shè)置4字節(jié)對(duì)齊???

//#pragma?pack()?????//取消4字節(jié)對(duì)齊???


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<

????S1?s1;??

????S2?s2;??

????S3?s3;??

????S4?s4;??

????S5?s5;??

cout<

return?0;??

}?

輸出結(jié)果?:

1?2?4?4?8??

8?12?8?16?32??


下面解釋一下其中的幾個(gè)結(jié)構(gòu)體字節(jié)分配的情況

比如對(duì)于node2

typedef?struct?node2??

{??

????chara;??

????intb;??

????shortc;??

}S2

sizeof(S2)=12;

對(duì)于變量a门躯,它的自身對(duì)齊參數(shù)為1,#pragma pack(n)默認(rèn)值為8酷师,則最終a的對(duì)齊參數(shù)為1讶凉,為其分配1字節(jié)的空間,它相對(duì)于結(jié)構(gòu)體起始地址的偏移量為0山孔,能被4整除懂讯;

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

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

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

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

|char|----|----|----|??4字節(jié)??

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

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

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

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

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

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

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

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

typedef?struct?node5??

{??

bool?a;??

????S1?s1;??

double?b;??

int?c;??

}S5;??

sizeof(S5)=32呀酸。

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

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

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

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

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

對(duì)于整個(gè)結(jié)構(gòu)體來(lái)說(shuō)薯酝,各個(gè)變量的最終對(duì)齊參數(shù)為1半沽,4,8吴菠,4者填,最大值為8,#pragma pack(n)默認(rèn)值為8做葵,所以最終結(jié)構(gòu)體的大小必須是8的倍數(shù)占哟,因此需要在最后面填充4字節(jié)達(dá)到32字節(jié)。其存儲(chǔ)如下:

|--------bool--------|???4字節(jié)??

|---------s1---------|???8字節(jié)??

|--------------------|????4字節(jié)??

|--------double------|???8字節(jié)??

|----int----|---------|????8字節(jié)??

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

當(dāng)把系統(tǒng)默認(rèn)對(duì)齊參數(shù)設(shè)置為4時(shí),即#pragma pack(n)

輸出結(jié)果是:

1?2?4?4?8??

8?12?8?16?24 ??

針對(duì) 結(jié)構(gòu)體所占字節(jié)數(shù) 一定要注意對(duì)齊驳糯;

在沒(méi)有pragma pack(n)情況下篇梭,所占字節(jié)數(shù)會(huì)是 類型中最大對(duì)齊參數(shù)的倍數(shù);

在有pragma pack(n)下酝枢,所占字節(jié)數(shù)一定是n值的倍數(shù)恬偷。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市帘睦,隨后出現(xiàn)的幾起案子袍患,更是在濱河造成了極大的恐慌,老刑警劉巖竣付,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诡延,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡古胆,警方通過(guò)查閱死者的電腦和手機(jī)肆良,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)逸绎,“玉大人惹恃,你說(shuō)我怎么就攤上這事」啄粒” “怎么了巫糙?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)颊乘。 經(jīng)常有香客問(wèn)我曲秉,道長(zhǎng)采蚀,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任承二,我火速辦了婚禮榆鼠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘亥鸠。我一直安慰自己妆够,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布负蚊。 她就那樣靜靜地躺著神妹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪家妆。 梳的紋絲不亂的頭發(fā)上鸵荠,一...
    開(kāi)封第一講書(shū)人閱讀 51,763評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音伤极,去河邊找鬼蛹找。 笑死,一個(gè)胖子當(dāng)著我的面吹牛哨坪,可吹牛的內(nèi)容都是我干的庸疾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼当编,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼届慈!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起忿偷,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤金顿,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后鲤桥,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體串绩,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年芜壁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片高氮。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡慧妄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出剪芍,到底是詐尸還是另有隱情塞淹,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布罪裹,位于F島的核電站饱普,受9級(jí)特大地震影響运挫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜套耕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一谁帕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧冯袍,春花似錦匈挖、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至征冷,卻和暖如春择膝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背检激。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工肴捉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人呵扛。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓每庆,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親今穿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子缤灵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容