淺(qian)談字節(jie)對齊
時間(jian):2017-11-21 來源:未知(zhi)
前言
對于(yu)字(zi)節(jie)對齊,是每個(ge)(ge)初學C語(yu)言者(zhe)都比(bi)較容易暈的(de)(de)(de)知(zhi)識點(dian),也是很多的(de)(de)(de)公司在招聘(pin)的(de)(de)(de)時候經常(chang)考到的(de)(de)(de)知(zhi)識點(dian)。但這(zhe)個(ge)(ge)知(zhi)識點(dian)對我們(men)程序的(de)(de)(de)優化以及理(li)解計算機的(de)(de)(de)運行原理(li)又是至關(guan)重要的(de)(de)(de)。所(suo)以今天,我們(men)來這(zhe)個(ge)(ge)知(zhi)識點(dian)進行一下總結。
1. 為什么要字節對齊
簡單來講,就是(shi)為了提(ti)高數(shu)據(ju)讀取的(de)效(xiao)率。那為什(shen)么字節對齊會提(ti)高數(shu)據(ju)存取的(de)效(xiao)率呢?主要原(yuan)因是(shi)下面兩個方面:
1) 外部總(zong)線(xian),從內存(cun)(cun)中(zhong)獲取(qu)數(shu)(shu)據(ju),并不是(shi)(shi)按(an)照數(shu)(shu)據(ju)存(cun)(cun)儲的(de)小單位字(zi)節(jie)來(lai)的(de),而是(shi)(shi)4字(zi)節(jie)、8字(zi)節(jie)甚至更多,那這(zhe)(zhe)樣,假(jia)如當前計算機是(shi)(shi)按(an)照4字(zi)節(jie)讀(du)取(qu)數(shu)(shu)據(ju)的(de),那么,如果(guo)一(yi)個int類型的(de)數(shu)(shu)據(ju),在字(zi)節(jie)對齊(qi)的(de)位置開始存(cun)(cun)放(fang),則(ze)可以(yi)一(yi)次讀(du)取(qu)到(dao),否則(ze)需要花兩次才能取(qu)到(dao)這(zhe)(zhe)個數(shu)(shu)據(ju)。
2) 很(hen)多的(de)cpu只從對齊的(de)地址開(kai)始加載數(shu)據,而(er)有(you)的(de)CPU這樣做,就是為了更快一些。
2. 字節對齊的原則
1). 變量(liang)對齊
變量地址 % min(變量字(zi)節數, 機器位數/8) = 0
例:int a;
上邊的例子等價(jia)于:變(bian)量地址(zhi)%4=0
也就(jiu)是,計(ji)算機會為我(wo)們開辟一塊,長度為4字節,地址是4的整數(shu)倍的一塊地址空(kong)間。
2). 結構體(ti)成員(yuan)變(bian)量對齊
結構(gou)體成(cheng)員(yuan)變量(liang)地址 % min(變量(liang)字節數, 機器位(wei)數/8) = 0;
理(li)解同1)。
3). 結構體變(bian)量對齊(qi)
結(jie)構體(ti)成員變量(liang)的大(da)對齊方式相同(tong)
例:
struct test
{
char a;
int b;
short c;
}test;
上邊的(de)結構體(ti)中,大的(de)對齊(qi)方式為(wei)4,則(ze)整個結構體(ti)的(de)對齊(qi)方式為(wei)4,也就是結構體(ti)的(de)內存地址是4的(de)整數(shu)倍。
4). 結(jie)構體成員變量偏移對齊
結構體成員(yuan)變量(liang)偏(pian)移 % min(變量(liang)字節數(shu), 機器位(wei)數(shu)/8) = 0;
3. 詳細實例
struct test
{
char a;
int b;
short c;
}test;
上(shang)邊的結構體變量定義好之后,我們還是詳細的來看一下(xia),計(ji)算機為我們做了(le)什么(me)事情(qing)。
A:計算機首先,從一個是4的(de)整數倍(bei)的(de)地址開(kai)始準備(bei)開(kai)辟空間。(因為結構體的(de)對齊方式與(yu)結構體中成(cheng)員變量大的(de)結構體方式相同。)比如(ru)這(zhe)個地址值是0x7fff0100。
B: 成(cheng)(cheng)員變量a,char類型,根據對齊原則,這個(ge)成(cheng)(cheng)員變量的地(di)址(zhi)是1的整數倍即可。則是數組的地(di)址(zhi)即可。根據對齊,會(hui)一(yi)(yi)次開辟(pi)4字節(jie)(jie)的內存空間(jian),a占用(yong)第一(yi)(yi)個(ge)字節(jie)(jie)。
C: 成(cheng)員(yuan)變量(liang)b,int類型,根(gen)據對齊原則(ze),這(zhe)個成(cheng)員(yuan)變量(liang)的(de)(de)地址是(shi)4的(de)(de)整數倍即(ji)可。則(ze)原來開辟的(de)(de)4字節,已經沒有位置滿(man)足這(zhe)個變量(liang)的(de)(de)對齊了,則(ze)會(hui)重新分配一(yi)個四字節的(de)(de)內存空間(jian),將成(cheng)員(yuan)變量(liang)b放(fang)到這(zhe)個四字節中,正好放(fang)下(xia)。
D: 后(hou)還有一個(ge)(ge)short類型(xing)的(de)變量c,則系統又一次性的(de)分配4字(zi)(zi)節,short c占用其中的(de)兩個(ge)(ge)字(zi)(zi)節。
將(jiang)其(qi)裝在內存中如下:
+---+---+---+---+---+---+---+---+---+---+---+---+
| a | | b | c | |
+---+---+---+---+---+---+---+---+---+---+---+---+
那么這(zhe)個結構體的長度,也就是sizeof(test)=12;
那么接下來,我們換一(yi)種定義(yi)方式看一(yi)下:
struct test
{
char a;
short c;
int b;
}test;
+---+---+---+---+---+---+---+---+
| a | | c | b |
+---+---+---+---+---+---+---+---+
根據對齊原(yuan)則,就(jiu)是上邊的(de)這(zhe)種(zhong)存儲方式(shi),可以(yi)看到,這(zhe)種(zhong)方式(shi)的(de)定義,只(zhi)需(xu)要分配8字節的(de)內存空間就(jiu)夠了(le)。
那我們利用字(zi)節(jie)對(dui)齊,可(ke)以修改定(ding)義(yi)結構體的(de)順序,是可(ke)以達到節(jie)省空間的(de)效果的(de)。
另外,還會有一些場景(jing),我們(men)會通過指針的(de)(de)(de)偏(pian)(pian)移(yi)的(de)(de)(de)方式訪問結構體內存(cun)(cun)的(de)(de)(de),這(zhe)個(ge)時候,如果我們(men)存(cun)(cun)在(zai)空(kong)洞(dong),而寫指針偏(pian)(pian)移(yi)的(de)(de)(de)程序的(de)(de)(de)人沒有注意,是會非常容易出問題的(de)(de)(de),則這(zhe)種(zhong)場景(jing),我們(men)一般會人為的(de)(de)(de)將結構體中的(de)(de)(de)內存(cun)(cun)中的(de)(de)(de)空(kong)洞(dong),全部補齊。
比如,上邊兩種定義(yi),我們分別來將其補齊。
struct test
{
char a;
char szResv[3];
int b;
short c;
short sResv;
}test;
struct test
{
char a;
char cResv;
short c;
int b;
}test;

