內(nèi)存對齊

1、內(nèi)存對齊的原因

我們都知道計算機(jī)是以字節(jié)(Byte)為單位劃分的,理論上來說CPU是可以訪問任一編號的字節(jié)數(shù)據(jù)的,我們又知道CPU的尋址其實是通過地址總線來訪問內(nèi)存的,CPU又分為32位和64位,在32位的CPU一次可以處理4個字節(jié)(Byte)的數(shù)據(jù),那么CPU實際尋址的步長就是4個字節(jié),也就是只對編號是4的倍數(shù)的內(nèi)存地址進(jìn)行尋址。同理64位的CPU的尋址步長是8字節(jié),只對編號是8的倍數(shù)的內(nèi)存地址進(jìn)行尋址,如下圖所示是64位CPU的尋址示意圖:

image

這樣做可以實現(xiàn)最快速的方式尋址且不會遺漏一個字節(jié),也不會重復(fù)尋址。

那么對于程序而言,一個變量的數(shù)據(jù)存儲范圍是在一個尋址步長范圍內(nèi)的話,這樣一次尋址就可以讀取到變量的值,如果是超出了步長范圍內(nèi)的數(shù)據(jù)存儲,就需要讀取兩次尋址再進(jìn)行數(shù)據(jù)的拼接,效率明顯降低了。例如一個double類型的數(shù)據(jù)在內(nèi)存中占據(jù)8個字節(jié),如果地址是8,那么好辦,一次尋址就可以了,如果是20呢,那就需要進(jìn)行兩次尋址了。這樣就產(chǎn)生了數(shù)據(jù)對齊的規(guī)則,也就是將數(shù)據(jù)盡量的存儲在一個步長內(nèi),避免跨步長的存儲,這就是內(nèi)存對齊。在32位編譯環(huán)境下默認(rèn)4字節(jié)對齊,在64位編譯環(huán)境下默認(rèn)8字節(jié)對齊。

2、內(nèi)存對齊的規(guī)則

1、數(shù)據(jù)成員對齊規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員,第一個數(shù)據(jù)成員放在offset為0的地方,以后每個數(shù)據(jù)成員的對齊按照#pragma pack指定的數(shù)值和這個數(shù)據(jù)成員自身長度中,比較小的那個進(jìn)行。

2、結(jié)構(gòu)(或聯(lián)合)的整體對齊規(guī)則:在數(shù)據(jù)成員完成各自對齊之后,結(jié)構(gòu)(或聯(lián)合)本身也要進(jìn)行對齊,對齊將按照#pragma pack指定的數(shù)值和結(jié)構(gòu)(或聯(lián)合)最大數(shù)據(jù)成員長度中,比較小的那個進(jìn)行。

3、結(jié)構(gòu)體作為數(shù)據(jù)成員:如果一個結(jié)構(gòu)里有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲。

你一定會疑惑#pragma pack是個什么東西呢?#pragma pack其實就是指定內(nèi)存對齊系數(shù),如1,2,4,8,16。xcode默認(rèn)的對齊系數(shù)是8.

關(guān)于offset的說明:

  1. 結(jié)構(gòu)體的變量的首地址是其最長基本數(shù)據(jù)成員的整數(shù)倍。
    2.結(jié)構(gòu)體每個成員的相對于結(jié)構(gòu)體首地址的偏移量是其數(shù)據(jù)長度的整數(shù)倍,如有需要編譯器會在成員之間填充字節(jié)。

3、演練

在這里我只對8字節(jié)對齊進(jìn)行演練

struct struct_test1 {
    int a;//4字節(jié)
    char b;//1字節(jié)
    short c;//2字節(jié)
    char d[6];//1字節(jié),6個char的長度
    double e;//8字節(jié)
   struct struct_test2 {
        double f;//8字節(jié)
        char h;//1字節(jié)
        int j;//4字節(jié)
    }MyStruct2;
char j;//1字節(jié)
}MyStruct1;

規(guī)則1:數(shù)據(jù)成員對齊規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員,第一個數(shù)據(jù)成員放在offset為0的地方,以后每個數(shù)據(jù)成員的對齊按照#pragma pack指定的數(shù)值和這個數(shù)據(jù)成員自身長度中,比較小的那個進(jìn)行

int型,長度4 < 8 按4對齊;起始o(jì)ffset=0 ;存放位置區(qū)間[0,3]
char型,長度1<8按1對齊;起始o(jì)ffset=4 ;存放位置區(qū)間[4]
short型,長度2<8按2對齊;起始o(jì)ffset=6 ;存放位置區(qū)間[6,7]
char型,長度1<8按1對齊;起始o(jì)ffset=8 ;存放位置區(qū)間[8,13]
double型,長度8=8按8對齊;起始o(jì)ffset=16;存放位置區(qū)間[16,23]

規(guī)則3:結(jié)構(gòu)體作為數(shù)據(jù)成員:如果一個結(jié)構(gòu)里有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲

MyStruct2內(nèi)部成員最大長度為double8字節(jié),偏移量offset=24正好是8的倍數(shù),所以從24開始存儲MyStruct2
double型,長度8=8按8對齊,起始o(jì)ffset=24,存儲位置區(qū)間[24,31]
char型,長度1<8按1對齊,起始o(jì)ffset=32,存儲位置區(qū)間[32]
int 型,長度4<8按4對齊,起始o(jì)ffset=36,存儲位置區(qū)間[36,39]

規(guī)則2:結(jié)構(gòu)(或聯(lián)合)的整體對齊規(guī)則:在數(shù)據(jù)成員完成各自對齊之后,結(jié)構(gòu)(或聯(lián)合)本身也要進(jìn)行對齊,對齊將按照#pragma pack指定的數(shù)值和結(jié)構(gòu)(或聯(lián)合)最大數(shù)據(jù)成員長度中,比較小的那個進(jìn)行
整體對齊系數(shù) = min((max(double,char,int), 8) = 8,將內(nèi)存大小由39補(bǔ)齊到8的整數(shù)倍40

char型,長度1<8,其實offset=41,存儲位置區(qū)間[41]
整體對齊系數(shù) = min((max(int,short,char,double), 8) = 8,將內(nèi)存大小由41補(bǔ)齊到8的整數(shù)倍48
所以最終對齊后的大小為48字節(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容