一朱浴、什么是內(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
,長度為4
字節(jié)音同,在內(nèi)存中的地址是12~15
词爬。 - 最后是
short
,short
占用內(nèi)存2
字節(jié)权均,此時(shí)內(nèi)存中的起始位置
為16
顿膨,16%2==0
成立,所以short
在內(nèi)存中的起始位置是16
叽赊,長度為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)存分布圖
同理梅割,我們再來分析一下Struct2
的結(jié)果:
- 首先
Struct2
中以double
開始,在內(nèi)存中的地址是0~7
葛家。 - 然后是
int
户辞,int
占用內(nèi)存大小為4
字節(jié),此時(shí)在內(nèi)存中的起始位置
為8
癞谒,8%4 == 0
等式成立底燎,所以此時(shí)int
的起始位置為8
刃榨,長度為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
莲祸,長度為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
期虾,長度為24
,在內(nèi)存中的地址是16~39
驯嘱。 - 最后是
short
镶苞,short
占用內(nèi)存2
字節(jié),此時(shí)內(nèi)存中的起始位置
為40
鞠评,40%2==0
等式成立茂蚓,起始位置是40
,長度為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
晨汹,長度為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
,長度為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é)歡迎留言給我坐漏。