一、什么是內(nèi)存對(duì)齊?
內(nèi)存對(duì)齊是一種在計(jì)算機(jī)內(nèi)存中排列數(shù)據(jù)(表現(xiàn)為變量的地址)、訪問數(shù)據(jù)(表現(xiàn)為CPU讀取數(shù)據(jù))的一種方式。
它包含了兩種相互獨(dú)立又相互關(guān)聯(lián)的部分:基本數(shù)據(jù)對(duì)齊和結(jié)構(gòu)體數(shù)據(jù)對(duì)齊 。
二、為什么要進(jìn)行內(nèi)存對(duì)齊?
1、平臺(tái)原因(移植原因):不是所有的硬件平臺(tái)都能訪問任意地址上的任意數(shù)據(jù)的;某些硬件平臺(tái)只能在某些地址處取某些特定類型的數(shù)據(jù),否則拋出硬件異常。
2、性能原因:數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能地在自然邊界上對(duì)齊。原因在于,為了訪問未對(duì)齊的內(nèi)存,處理器需要作兩次內(nèi)存訪問;而對(duì)齊的內(nèi)存訪問僅需要一次訪問。
說的通俗點(diǎn)就是方便讀取,速度快。
三、內(nèi)存對(duì)齊原則:
1、數(shù)據(jù)成員對(duì)?規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員,第一個(gè)數(shù)據(jù)成員放在offset為0的地方,以后每個(gè)數(shù)據(jù)成員存儲(chǔ)的起始位置要從該成員大小或者成員的子成員大小(只要該成員有子成員,比如說是數(shù)組,結(jié)構(gòu)體等)的整數(shù)倍開始(比如int為4字節(jié),則要從4的整數(shù)倍地址開始存儲(chǔ))。下面咱們用(m, n)來理解一下,其中m為內(nèi)存的起始位置,n為當(dāng)前成員的內(nèi)存大小,m,n一定要滿足m%n==0。
2、結(jié)構(gòu)體作為成員:如果一個(gè)結(jié)構(gòu)里有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲(chǔ)(struct a里存有struct b,b里有char、int 、double等元素,那b應(yīng)該從8的整數(shù)倍開始存儲(chǔ)。)
3、收尾工作:結(jié)構(gòu)體的總大小,也就是sizeof的結(jié)果,必須是其內(nèi)部最大成員的整數(shù)倍,不足的要補(bǔ)?。
各位同學(xué),能理解上面這些原則嗎,反正光看文字我是理解不了,沒關(guān)系,咱們是程序員,咱們可以用代碼來解釋。
四、數(shù)據(jù)成員結(jié)構(gòu)體內(nèi)存分析
struct Struct1 {
double a; // 8
char b; // 1
int c; // 4
short d; // 2
}struct1;
struct Struct2 {
double a; //8
int b; //4
char c; //1
short d; //2
}struct2;
NSLog(@"結(jié)果為:%lu-%lu",sizeof(struct1),sizeof(struct2));
咱們來看下打印結(jié)果
結(jié)果為:24-16
為什么會(huì)出現(xiàn)不同的結(jié)果呢,咱們來分析一下,不過在分析代碼之前呢咱們先看下各個(gè)類型的內(nèi)存大小

下面咱們來分析一下
Struct1的結(jié)果:
- 首先
Struct1中以double開始,在內(nèi)存中的地址是0~7。 - 接下來是
char,char占用內(nèi)存大小為1字節(jié),此時(shí)在內(nèi)存中的起始地址為8,此時(shí)8%1==0表示可以從此位置開始存放char,在內(nèi)存中的地址是8。 - 然后是
int,int占用內(nèi)存大小為4字節(jié),此時(shí)在內(nèi)存中的起始位置為9,但是現(xiàn)在9%4 != 0,所以此時(shí)只能向后移,找到一個(gè)能被4整除的數(shù),下一個(gè)能被4整除的數(shù)是12,所以此時(shí)int的起始位置為12,長(zhǎng)度為4字節(jié),在內(nèi)存中的地址是12~15。 - 最后是
short,short占用內(nèi)存2字節(jié),此時(shí)內(nèi)存中的起始位置為16,16%2==0成立,所以short在內(nèi)存中的起始位置是16,長(zhǎng)度為2,在內(nèi)存中的地址是16~17。
由上述結(jié)果可得Struct1在內(nèi)存中占用18個(gè)字節(jié),根據(jù)內(nèi)存對(duì)齊第三條原則,Struct1內(nèi)部最大成員為double為8字節(jié),所以Struct1最終占用的內(nèi)存大小為24字節(jié)。由此可以得出下圖:Struct1內(nèi)存分布圖
同理,我們?cè)賮矸治鲆幌?code>Struct2的結(jié)果:
- 首先
Struct2中以double開始,在內(nèi)存中的地址是0~7。 - 然后是
int,int占用內(nèi)存大小為4字節(jié),此時(shí)在內(nèi)存中的起始位置為8,8%4 == 0等式成立,所以此時(shí)int的起始位置為8,長(zhǎng)度為4字節(jié),在內(nèi)存中的地址是8~11。 - 接下來是
char,char占用內(nèi)存大小為1字節(jié),此時(shí)在內(nèi)存中的起始地址為12,此時(shí)12%1==0等式成立表示可以從此位置開始存放char,在內(nèi)存中的地址是12。 - 最后是
short,short占用內(nèi)存2字節(jié),此時(shí)內(nèi)存中的起始位置為13,13%2==0等式不成立,需要向后移,找到一個(gè)能被2整除的數(shù),所以short在內(nèi)存中的起始位置是14,長(zhǎng)度為2,在內(nèi)存中的地址是14~15。
有上述結(jié)果可得Struct2在內(nèi)存中占用16個(gè)字節(jié),根據(jù)內(nèi)存對(duì)齊原則第三條,Struct2內(nèi)部最大成員為double為8字節(jié),所以Struct2最終占用的內(nèi)存大小為16字節(jié)。由此可得出下圖:
Struct2內(nèi)存分布圖
這就是Struct1和Struct2結(jié)果不同的原因。
由此我們可以得出結(jié)論:結(jié)構(gòu)體所占內(nèi)存大小與結(jié)構(gòu)體內(nèi)部的成員變量的順序有關(guān)。
五、嵌套結(jié)構(gòu)體內(nèi)存分析
struct Struct3 {
double a; //8
char b; //4
struct Struct1 c; //24
short d; //2
}struct3;
struct Struct4 {
double a; //8
char b; //1
short c; //2
struct Struct2 d; //16
}struct4;
NSLog(@"結(jié)果為:%lu-%lu",sizeof(struct3),sizeof(struct4));
咱們來看下打印結(jié)果
結(jié)果為:48-32
我們來分析下Struct3的結(jié)果:
- 首先
Struct3中以double開始,在內(nèi)存中的地址是0~7。 - 接下來是
char,char占用內(nèi)存大小為1字節(jié),此時(shí)在內(nèi)存中的起始地址為8,此時(shí)8%1==0等式成立表示可以從此位置開始存放char,在內(nèi)存中的地址是8,在內(nèi)存中的地址是8。 - 然后是結(jié)構(gòu)體
Struct1,由上面的結(jié)論得知Struct1的大小為24字節(jié),此時(shí)在內(nèi)存中的起始地址為9,根據(jù)內(nèi)存對(duì)齊原則第二條得知需從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲(chǔ),Struct1中最大的為double占8字節(jié),9%8 == 0不成立,所以取最近的一個(gè)能被8整除的數(shù)為16,所以Struct1在內(nèi)存中的起始位置為16,長(zhǎng)度為24,在內(nèi)存中的地址是16~39。 - 最后是
short,short占用內(nèi)存2字節(jié),此時(shí)內(nèi)存中的起始位置為40,40%2==0等式成立,起始位置是40,長(zhǎng)度為2,在內(nèi)存中的地址是40~41。
有上述結(jié)果可得Struct3在內(nèi)存中占用42個(gè)字節(jié),根據(jù)內(nèi)存對(duì)齊原則,Struct3內(nèi)部包括子成員Struct1內(nèi)部最大成員為double為8字節(jié),所以Struct3最終占用的內(nèi)存大小為48字節(jié)。由此可得出下圖:Struct3內(nèi)存分布圖
我們來分析下Struct4的結(jié)果:
- 首先
Struct4中以double開始,在內(nèi)存中的地址是0~7。 - 接下來是
char,char占用內(nèi)存大小為1字節(jié),此時(shí)在內(nèi)存中的起始地址為8,此時(shí)8%1==0等式成立表示可以從此位置開始存放char,在內(nèi)存中的地址是8,在內(nèi)存中的地址是8。 - 然后是
short,short占用內(nèi)存2字節(jié),此時(shí)內(nèi)存中的起始位置為9,9%2==0等式不成立,其后能被2整除的數(shù)為10,那么起始位置是10,長(zhǎng)度為2,在內(nèi)存中的地址是10~11。 - 最后是結(jié)構(gòu)體
Struct2,由上面的結(jié)論得知Struct2的大小為16字節(jié),此時(shí)在內(nèi)存中的起始地址為12,根據(jù)內(nèi)存對(duì)齊原則第二條得知需從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲(chǔ),Struct2中最大的為double占8字節(jié),12%8 == 0不成立,所以取最近的一個(gè)能被8整除的數(shù)為16,所以Struct2在內(nèi)存中的起始位置為16,長(zhǎng)度為16,在內(nèi)存中的地址是16~31。
有上述結(jié)果可得Struct4在內(nèi)存中占用32個(gè)字節(jié),根據(jù)內(nèi)存對(duì)齊原則,Struct4內(nèi)部包括子成員Struct1內(nèi)部最大成員為double為8字節(jié),所以Struct4最終占用的內(nèi)存大小為32字節(jié)。由此可得出下圖:Struct4內(nèi)存分布圖
以上就是我對(duì)內(nèi)存對(duì)齊原則的理解,如果有不同意見的同學(xué)歡迎留言給我。



