解析C語言結(jié)構(gòu)體對齊(內(nèi)存對齊問題)

解析C語言結(jié)構(gòu)體對齊(內(nèi)存對齊問題)

C語言結(jié)構(gòu)體對齊也是老生常談的話題了。基本上是面試題的必考題圾叼。內(nèi)容雖然很基礎(chǔ)榨惠,但一不小心就會弄錯。寫出一個struct憾赁,然后sizeof污朽,你會不會經(jīng)常對結(jié)果感到奇怪?sizeof的結(jié)果往往都比你聲明的變量總長度要大龙考,這是怎么回事呢蟆肆?

????開始學(xué)的時候,也被此類問題困擾很久晦款。其實相關(guān)的文章很多炎功,感覺說清楚的不多。結(jié)構(gòu)體到底怎樣對齊缓溅?

????有人給對齊原則做過總結(jié)蛇损,具體在哪里看到現(xiàn)在已記不起來,這里引用一下前人的經(jīng)驗(在沒有#pragma pack宏的情況下):

????原則1、數(shù)據(jù)成員對齊規(guī)則:結(jié)構(gòu)(struct或聯(lián)合union)的數(shù)據(jù)成員淤齐,第一個數(shù)據(jù)成員放在offset為0的地方束世,以后每個數(shù)據(jù)成員存儲的起始位置要從該成員大小的整數(shù)倍開始(比如int在32位機為4字節(jié),則要從4的整數(shù)倍地址開始存儲)床玻。

????原則2毁涉、結(jié)構(gòu)體作為成員:如果一個結(jié)構(gòu)里有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲锈死。(struct a里存有struct b贫堰,b里有char,int待牵,double等元素其屏,那b應(yīng)該從8的整數(shù)倍開始存儲。)

????原則3缨该、收尾工作:結(jié)構(gòu)體的總大小偎行,也就是sizeof的結(jié)果,必須是其內(nèi)部最大成員的整數(shù)倍贰拿,不足的要補齊蛤袒。

????這三個原則具體怎樣理解呢?我們看下面幾個例子膨更,通過實例來加深理解妙真。

例1:struct {

short a1;

short a2;

short a3;

}A;

struct{

long a1;

short a2;

}B;

??????????sizeof(A) = 6; 這個很好理解,三個short都為2荚守。

??????????sizeof(B) = 8; 這個比是不是比預(yù)想的大2個字節(jié)珍德?long為4,short為2矗漾,整個為8锈候,因為原則3。

例2:struct A{

int a;

char b;

short c;

};

struct B{

char b;

int a;

short c;

};

???????sizeof(A) = 8; int為4敞贡,char為1泵琳,short為2,這里用到了原則1和原則3嫡锌。

???????sizeof(B) = 12; 是否超出預(yù)想范圍虑稼?char為1,int為4势木,short為2,怎么會是12歌懒?還是原則1和原則3啦桌。

???深究一下,為什么是這樣,我們可以看看內(nèi)存里的布局情況甫男。

a?????????b?????????c

A的內(nèi)存布局:1111,?????1*,???????11

b??????????a????????c

B的內(nèi)存布局:1***,?????1111,???11**

???其中星號*表示填充的字節(jié)且改。A中,b后面為何要補充一個字節(jié)板驳?因為c為short又跛,其起始位置要為2的倍數(shù),就是原則1若治。c的后面沒有補充慨蓝,因為b和c正好占用4個字節(jié),整個A占用空間為4的倍數(shù)端幼,也就是最大成員int類型的倍數(shù)礼烈,所以不用補充。

???B中婆跑,b是char為1此熬,b后面補充了3個字節(jié),因為a是int為4滑进,根據(jù)原則1犀忱,起始位置要為4的倍數(shù),所以b后面要補充3個字節(jié)扶关。c后面補充兩個字節(jié)峡碉,根據(jù)原則3,整個B占用空間要為4的倍數(shù)驮审,c后面不補充鲫寄,整個B的空間為10,不符疯淫,所以要補充2個字節(jié)地来。

???再看一個結(jié)構(gòu)中含有結(jié)構(gòu)成員的例子:

例3:struct A{

int a;

double b;

float c;

};

struct B{

char e[2];

int f;

double g;

short h;

struct A i;

};

???????sizeof(A) = 24; 這個比較好理解,int為4熙掺,double為8未斑,float為4,總長為8的倍數(shù)币绩,補齊蜡秽,所以整個A為24。

???????sizeof(B) = 48; 看看B的內(nèi)存布局缆镣。

e?????????f?????????????g????????????????h????????????????????????????????????i

B的內(nèi)存布局:11* *,???1111,???11111111, 11 * * * * * *,????????1111* * * *, 11111111, 1111 * * * *

????i其實就是A的內(nèi)存布局芽突。i的起始位置要為24的倍數(shù),所以h后面要補齊董瞻。把B的內(nèi)存布局弄清楚寞蚌,有關(guān)結(jié)構(gòu)體的對齊方式基本就算掌握了田巴。

????以上講的都是沒有#pragma pack宏的情況,如果有#pragma pack宏挟秤,對齊方式按照宏的定義來壹哺。比如上面的結(jié)構(gòu)體前加#pragma pack(1),內(nèi)存的布局就會完全改變艘刚。sizeof(A) = 16; sizeof(B) = 32;

????有了#pragma pack(1)管宵,內(nèi)存不會再遵循原則1和原則3了,按1字節(jié)對齊攀甚。沒錯箩朴,這不是理想中的沒有內(nèi)存對齊的世界嗎。

a????????????????b?????????????c

A的內(nèi)存布局:1111,?????11111111,???1111

e????????f?????????????g??????????h?????????????????????i

B的內(nèi)存布局:11,???1111,???11111111, 11 ,????????????1111, 11111111, 1111

???????那#pragma pack(2)的結(jié)果又是多少呢云稚?#pragma pack(4)呢隧饼?留給大家自己思考吧,相信沒有問題静陈。

???????還有一種常見的情況燕雁,結(jié)構(gòu)體中含位域字段。位域成員不能單獨被取sizeof值鲸拥。C99規(guī)定int拐格、unsigned int和bool可以作為位域類型,但編譯器幾乎都對此作了擴展刑赶,允許其它類型類型的存在捏浊。

使用位域的主要目的是壓縮存儲,其大致規(guī)則為:

1) 如果相鄰位域字段的類型相同撞叨,且其位寬之和小于類型的sizeof大小金踪,則后面的字段將緊鄰前一個字段存儲,直到不能容納為止牵敷;

2) 如果相鄰位域字段的類型相同胡岔,但其位寬之和大于類型的sizeof大小,則后面的字段將從新的存儲單元開始枷餐,其偏移量為其類型大小的整數(shù)倍靶瘸;

3) 如果相鄰的位域字段的類型不同,則各編譯器的具體實現(xiàn)有差異毛肋,VC6采取不壓縮方式怨咪,Dev-C++采取壓縮方式;

4) 如果位域字段之間穿插著非位域字段润匙,則不進行壓縮诗眨;

5) 整個結(jié)構(gòu)體的總大小為最寬基本類型成員大小的整數(shù)倍。

????????還是讓我們來看看例子趁桃。

例4:struct A{

char f1 : 3;

char f2 : 4;

char f3 : 5;

};

a????????b?????????????c

A的內(nèi)存布局:111,????1111 *,???11111 * * *

???????位域類型為char辽话,第1個字節(jié)僅能容納下f1和f2肄鸽,所以f2被壓縮到第1個字節(jié)中卫病,而f3只能從下一個字節(jié)開始油啤。因此sizeof(A)的結(jié)果為2。

例5:struct B{

char f1 : 3;

short f2 : 4;

char f3 : 5;

};

???????由于相鄰位域類型不同蟀苛,在VC6中其sizeof為6益咬,在Dev-C++中為2。

例6:struct C{

char f1 : 3;

char f2;

char f3 : 5;

};

???????非位域字段穿插在其中帜平,不會產(chǎn)生壓縮幽告,在VC6和Dev-C++中得到的大小均為3。

???????考慮一個問題裆甩,為什么要設(shè)計內(nèi)存對齊的處理方式呢冗锁?如果體系結(jié)構(gòu)是不對齊的,成員將會一個挨一個存儲嗤栓,顯然對齊更浪費了空間冻河。那么為什么要使用對齊呢?體系結(jié)構(gòu)的對齊和不對齊茉帅,是在時間和空間上的一個權(quán)衡叨叙。對齊節(jié)省了時間。假設(shè)一個體系結(jié)構(gòu)的字長為w堪澎,那么它同時就假設(shè)了在這種體系結(jié)構(gòu)上對寬度為w的數(shù)據(jù)的處理最頻繁也是最重要的擂错。它的設(shè)計也是從優(yōu)先提高對w位數(shù)據(jù)操作的效率來考慮的。有興趣的可以google一下樱蛤,人家就可以跟你解釋的钮呀,一大堆的道理。

???????最后順便提一點昨凡,在設(shè)計結(jié)構(gòu)體的時候爽醋,一般會尊照一個習(xí)慣,就是把占用空間小的類型排在前面土匀,占用空間大的類型排在后面子房,這樣可以相對節(jié)約一些對齊空間。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末就轧,一起剝皮案震驚了整個濱河市证杭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌妒御,老刑警劉巖解愤,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異乎莉,居然都是意外死亡送讲,警方通過查閱死者的電腦和手機奸笤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哼鬓,“玉大人监右,你說我怎么就攤上這事∫煜#” “怎么了健盒?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長称簿。 經(jīng)常有香客問我扣癣,道長,這世上最難降的妖魔是什么憨降? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任父虑,我火速辦了婚禮,結(jié)果婚禮上授药,老公的妹妹穿的比我還像新娘士嚎。我一直安慰自己,他們只是感情好烁焙,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布航邢。 她就那樣靜靜地躺著,像睡著了一般骄蝇。 火紅的嫁衣襯著肌膚如雪膳殷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天九火,我揣著相機與錄音赚窃,去河邊找鬼。 笑死岔激,一個胖子當(dāng)著我的面吹牛勒极,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播虑鼎,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼辱匿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了炫彩?” 一聲冷哼從身側(cè)響起匾七,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎江兢,沒想到半個月后昨忆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡杉允,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年邑贴,在試婚紗的時候發(fā)現(xiàn)自己被綠了席里。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡拢驾,死狀恐怖奖磁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情独旷,我是刑警寧澤署穗,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布寥裂,位于F島的核電站嵌洼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏封恰。R本人自食惡果不足惜麻养,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望诺舔。 院中可真熱鬧鳖昌,春花似錦、人聲如沸低飒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽褥赊。三九已至糕档,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拌喉,已是汗流浹背速那。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留尿背,地道東北人端仰。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像田藐,于是被迫代替她去往敵國和親荔烧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

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