在C語(yǔ)言柔性數(shù)組一文中嘶朱,提到了內(nèi)存對(duì)齊蛾坯,于是想寫(xiě)篇文章總結(jié)總結(jié)內(nèi)存對(duì)齊。
內(nèi)存對(duì)齊
#include<stdio.h>
typedef struct line {
int len;
char *contents;
} line;
int main(int argc, char **argv)
{
printf("%d\n", sizeof(line));
}
/* 輸出: 16
* 平臺(tái): Linux x64;
*/
為什么需要內(nèi)存對(duì)齊
計(jì)算機(jī)系統(tǒng)對(duì)基本數(shù)據(jù)類型的合法地址做出了一些限制疏遏,要求某種類型對(duì)象的地址必須是某個(gè)值K(通常是2, 4或8)的倍數(shù)脉课,這種對(duì)齊限制簡(jiǎn)化了處理器和存儲(chǔ)器系統(tǒng)之間接口的硬件設(shè)計(jì)救军。
假設(shè)一個(gè)CPU總是從存儲(chǔ)器中一次讀出8個(gè)字節(jié)的塊(塊大小一般稱之為為memory access granularity(粒度)),則地址必須為8的倍數(shù)倘零,如果我們能保證多有的double類型地址對(duì)齊為8的倍數(shù)唱遭,那么我們只需要訪問(wèn)一次存儲(chǔ)器就能取得我們想要的數(shù)據(jù),否則视事,我們可能需要訪問(wèn)存儲(chǔ)器2次胆萧,因?yàn)閷?duì)象可能放在了2個(gè)8字節(jié)的存儲(chǔ)器塊中。
內(nèi)存對(duì)齊規(guī)則
Linux 沿用的對(duì)齊策略是俐东,2字節(jié)數(shù)據(jù)類型(short)的地址必須是2的倍數(shù)跌穗,而較大的數(shù)據(jù)類型(例如int,int*虏辫,float蚌吸,double)的地址必須是4的倍數(shù)。而Windows對(duì)齊要求更嚴(yán)格砌庄,它要求double羹唠,long long類型數(shù)據(jù)應(yīng)該是8的倍數(shù)。
struct s1 {
int i;
char c;
int j;
};
假設(shè)編譯器最小9字節(jié)分配如圖:
它不可能滿足i和j(偏移為5)的4字節(jié)對(duì)齊要求娄昆。所以編譯器要在c和j之間插入一個(gè)3字節(jié)的間隙佩微,如圖所示:
另外編譯器結(jié)構(gòu)的末尾也可能需要填充:
struct s2 {
int i;
int j;
char c;
};
如果我們將這個(gè)結(jié)構(gòu)分配為9字節(jié),只要保證結(jié)構(gòu)體的起始地址滿足4字節(jié)對(duì)齊要求萌焰,我們?nèi)匀豢梢詽M足字段i和j的對(duì)齊要求哺眯。但考慮如下:
struct s2 arr[4];
如果每個(gè)結(jié)構(gòu)體分配9字節(jié),不可能滿足arr的每個(gè)元素的對(duì)齊要求扒俯。例如i奶卓,則地址分別為arr,arr+9撼玄,arr+18夺姑,arr+27。所以編譯器會(huì)為結(jié)構(gòu)體s2分配12個(gè)字節(jié)掌猛。如圖:
練習(xí)
struct p1 { int i; char c; int j; char d; };
struct p2 { };
struct p3 { };
struct p4 { };
struct p5 { };