四字节内存对齐
首先我們先看看下面的C語(yǔ)言的結(jié)構(gòu)體:
[cpp]?view plain?copy????以上這個(gè)結(jié)構(gòu)體占用內(nèi)存多少空間呢?也許你會(huì)說(shuō),這個(gè)簡(jiǎn)單,計(jì)算每個(gè)類(lèi)型的大小,將它們相加就行了,以32為平臺(tái)為例,int類(lèi)型占4字節(jié),char占用1字節(jié),所以:4 + 3 + 4 = 11,那么這個(gè)結(jié)構(gòu)體一共占用11字節(jié)空間。好吧,那么我們就用實(shí)踐來(lái)證明是否正確,我們用sizeof運(yùn)算符來(lái)求出這個(gè)結(jié)構(gòu)體占用內(nèi)存空間大小,sizeof(MemAlign),出乎意料的是,結(jié)果居然為12?看來(lái)我們錯(cuò)了?當(dāng)然不是,而是這個(gè)結(jié)構(gòu)體被優(yōu)化了,這個(gè)優(yōu)化有個(gè)另外一個(gè)名字叫“對(duì)齊”,那么這個(gè)對(duì)齊到底做了什么樣的優(yōu)化呢,聽(tīng)我慢慢解釋,再解釋之前我們先看一個(gè)圖,圖如下:
????相信學(xué)過(guò)匯編的朋友都很熟悉這張圖,這張圖就是CPU與內(nèi)存如何進(jìn)行數(shù)據(jù)交換的模型,其中,左邊藍(lán)色的方框是CPU,右邊綠色的方框是內(nèi)存,內(nèi)存上面的0~3是內(nèi)存地址。這里我們這張圖是以32位CPU作為代表,我們都知道,32位CPU是以雙字(DWORD)為單位進(jìn)行數(shù)據(jù)傳輸?shù)?#xff0c;也正因?yàn)檫@點(diǎn),造成了另外一個(gè)問(wèn)題,那么這個(gè)問(wèn)題是什么呢?這個(gè)問(wèn)題就是,既然32位CPU以雙字進(jìn)行數(shù)據(jù)傳輸,那么,如果我們的數(shù)據(jù)只有8位或16位數(shù)據(jù)的時(shí)候,是不是CPU就按照我們數(shù)據(jù)的位數(shù)來(lái)進(jìn)行數(shù)據(jù)傳輸呢?其答案是否定的,如果這樣會(huì)使得CPU硬件變的更復(fù)雜,所以32位CPU傳輸數(shù)據(jù)無(wú)論是8位或16位都是以雙字進(jìn)行數(shù)據(jù)傳輸。那么也罷,8位或16位一樣可以傳輸,但是,事情并非像我們想象的那么簡(jiǎn)單,比如,一個(gè)int類(lèi)型4字節(jié)的數(shù)據(jù)如果放在上圖內(nèi)存地址1開(kāi)始的位置,那么這個(gè)數(shù)據(jù)占用的內(nèi)存地址為1~4,那么這個(gè)數(shù)據(jù)就被分為了2個(gè)部分,一個(gè)部分在地址0~3中,另外一部分在地址4~7中,又由于32位CPU以雙字進(jìn)行傳輸,所以,CPU會(huì)分2次進(jìn)行讀取,一次先讀取地址0~3中內(nèi)容,再一次讀取地址4~7中數(shù)據(jù),最后CPU提取并組合出正確的int類(lèi)型數(shù)據(jù),舍棄掉無(wú)關(guān)數(shù)據(jù)。那么反過(guò)來(lái),如果我們把這個(gè)int類(lèi)型4字節(jié)的數(shù)據(jù)放在上圖從地址0開(kāi)始的位置會(huì)怎樣呢?讀到這里,也許你明白了,CPU只要進(jìn)行一次讀取就可以得到這個(gè)int類(lèi)型數(shù)據(jù)了。沒(méi)錯(cuò),就是這樣,這次CPU只用了一個(gè)周期就得到了數(shù)據(jù),由此可見(jiàn),對(duì)內(nèi)存數(shù)據(jù)的擺放是多么重要啊,擺放正確位置可以減少CPU的使用資源。
那么,內(nèi)存對(duì)齊有哪些原則呢?我總結(jié)了一下大致分為三條:
第一條:第一個(gè)成員的首地址為0
第二條:每個(gè)成員的首地址是自身大小的整數(shù)倍
???????第二條補(bǔ)充:以4字節(jié)對(duì)齊為例,如果自身大小大于4字節(jié),都以4字節(jié)整數(shù)倍為基準(zhǔn)對(duì)齊。
第三條:最后以結(jié)構(gòu)總體對(duì)齊。
????????第三條補(bǔ)充:以4字節(jié)對(duì)齊為例,取結(jié)構(gòu)體中最大成員類(lèi)型倍數(shù),如果超過(guò)4字節(jié),都以4字節(jié)整數(shù)倍為基準(zhǔn)對(duì)齊。(其中這一條還有個(gè)名字叫:“補(bǔ)齊”,補(bǔ)齊的目的就是多個(gè)結(jié)構(gòu)變量挨著擺放的時(shí)候也滿(mǎn)足對(duì)齊的要求。)
????上述的三原則聽(tīng)起來(lái)還是比較抽象,那么接下來(lái)我們通過(guò)一個(gè)例子來(lái)加深對(duì)內(nèi)存對(duì)齊概念的理解,下面是一個(gè)結(jié)構(gòu)體,我們動(dòng)手算出下面結(jié)構(gòu)體一共占用多少內(nèi)存?假設(shè)我們以32位平臺(tái)并且以4字節(jié)對(duì)齊方式:
[cpp]?view plain?copy我們就以這個(gè)圖來(lái)講解是如何對(duì)齊的:
第一個(gè)成員(char a[18]):首先,假設(shè)我們把它放到內(nèi)存開(kāi)始地址為0的位置,由于第一個(gè)成員占18個(gè)字節(jié),所以第一個(gè)成員占用內(nèi)存地址范圍為0~18。
第二個(gè)成員(double b):由于double類(lèi)型占8字節(jié),又因?yàn)?字節(jié)大于4字節(jié),所以就以4字節(jié)對(duì)齊為基準(zhǔn)。由于第一個(gè)成員結(jié)束地址為18,那么地址18并不是4的整數(shù)倍,我們需要再加2個(gè)字節(jié),也就是從地址20開(kāi)始擺放第二個(gè)成員。
第三個(gè)成員(char c):由于char類(lèi)型占1字節(jié),任意地址是1字節(jié)的整數(shù)倍,所以我們就直接將其擺放到緊接第二個(gè)成員之后即可。
第四個(gè)成員(int d):由于int類(lèi)型占4字節(jié),但是地址29并不是4的整數(shù)倍,所以我們需要再加3個(gè)字節(jié),也就是從地址32開(kāi)始擺放這個(gè)成員。
第五個(gè)成員(short e):由于short類(lèi)型占2字節(jié),地址36正好是2的整數(shù)倍,這樣我們就可以直接擺放,無(wú)需填充字節(jié),緊跟其后即可。
????這樣我們內(nèi)存對(duì)齊就完成了。但是離成功還差那么一步,那是什么呢?對(duì),是對(duì)整個(gè)結(jié)構(gòu)體補(bǔ)齊,接下來(lái)我們就補(bǔ)齊整個(gè)結(jié)構(gòu)體。那么,先讓我們回顧一下補(bǔ)齊的原則:“以4字節(jié)對(duì)齊為例,取結(jié)構(gòu)體中最大成員類(lèi)型倍數(shù),如果超過(guò)4字節(jié),都以4字節(jié)整數(shù)倍為基準(zhǔn)對(duì)齊。”在這個(gè)結(jié)構(gòu)體中最大類(lèi)型為double類(lèi)型(占8字節(jié)),又由于8字節(jié)大于4字?節(jié),所以我們還是以4字節(jié)補(bǔ)齊為基準(zhǔn),整個(gè)結(jié)構(gòu)體結(jié)束地址為38,而地址38并不是4的整數(shù)倍,所以我們還需要加額外2個(gè)字節(jié)來(lái)填充結(jié)構(gòu)體,如下圖紅色的就是補(bǔ)齊出來(lái)的空間:
到此為止,我們內(nèi)存對(duì)齊與補(bǔ)齊就完畢了!接下來(lái)我們用實(shí)驗(yàn)來(lái)證明真理,程序如下:
程序運(yùn)行過(guò)程中,查看內(nèi)存如下:
其中,各種顏色帶下劃線(xiàn)的代表各個(gè)成員變量,藍(lán)色方框的代表為內(nèi)存對(duì)齊時(shí)候填補(bǔ)的多余字節(jié),由于這里看不到補(bǔ)齊效果,我們接下來(lái)看下圖,下圖籃框包圍的字節(jié)就是與上圖的交集以外的部分就是補(bǔ)齊所填充的字節(jié)。
在最后,我在談一談關(guān)于補(bǔ)齊的作用,補(bǔ)齊其實(shí)就是為了讓這個(gè)結(jié)構(gòu)體定義的數(shù)組變量時(shí)候,數(shù)組內(nèi)部,也同樣滿(mǎn)足內(nèi)存對(duì)齊的要求,為了更好的理解這點(diǎn),我做了一個(gè)跟本例子相對(duì)照的圖:
總結(jié)
- 上一篇: 安装NFS服务,并挂载到开发板
- 下一篇: MOVW 和 rep