struct LGStruct1 {
char b;
int c;
double a;
short d;
}struct1;
struct LGStruct2 {
double a;
int c;
char b;
short d;
}struct2;
struct1 和struct2的內(nèi)存大小一致嘛,同樣的數(shù)據(jù)結(jié)構(gòu),內(nèi)存的大小應(yīng)該是一樣的呀!不要輕易的下結(jié)論,因為cpu在讀取數(shù)據(jù)的時候,都是一整塊一整塊的讀取,如果說每一塊的大小都不一樣,那么在每一次讀取的時候,還需要計算每一塊數(shù)據(jù)的大小,根據(jù)大小去讀取,這樣就浪費了時間的資源。所以,cpu在讀取數(shù)據(jù)的的時候,讀取的是一塊固定的大小,那么這個大小是怎么決定呢,根據(jù)數(shù)據(jù)中最大的單位去作為空間單元,這樣就可以把每個數(shù)據(jù)都裝進(jìn)去了,這就是通過空間來換取時間的便利。
每一個數(shù)據(jù)都分配同樣的大小嘛,并不是的,所以就引出了上面的經(jīng)典問題,兩個會一致嘛,其實打印內(nèi)存大小即可得知,struct1的內(nèi)存大小為24,struct2的內(nèi)存大小為16,這又是為什么呢,這里用空間換時間也不能極大的浪費,所以,這里就引入了一個內(nèi)存對齊的概念。
在這兩個結(jié)構(gòu)體中,占據(jù)內(nèi)存最大的類型為double,8個字節(jié),那么結(jié)構(gòu)體的每一塊都是為8字節(jié),struct1 排列順序為 char b; 1 字節(jié) int c; 4字節(jié) double a; 8字節(jié) short d; 2字節(jié)。 所以這里第一次分配的8字節(jié),先將char b 填充,此時這8個字節(jié)的內(nèi)存地址中,只有第一位裝了一個char類型,后面還有七個位置,是不是很浪費。所以將int c也填充到到后面的位置,應(yīng)該怎么放呢,這里其實是有規(guī)則的:
填充的位置必須可以整除該數(shù)據(jù)的大小,以int c為例,填充的起始坐標(biāo)就為4
后續(xù)的大小必須可以填充完整個數(shù)據(jù),同樣,4 ~ 7 4個坐標(biāo)可以放下4字節(jié)的int
所以,可以看看第一個分配的8字節(jié)都填充了什么數(shù)據(jù)
b 、、、c1 c2 c3 c4
0 1 2 3 4 5 6 7
同理,double a占據(jù)了第二個分配的8字節(jié)
此時 還有一個short 兩字節(jié)的,只能再分配一個8字節(jié)出來,這樣struct1分配的總大小即為24
分析struct2 double a占據(jù)了第一個分配的8字節(jié),第二個分配的8字節(jié)
c1 c2 c3 c4 b 、 d1 d2
0 1 2 3 4 5 6 7
所以struct2 分配的大小即為 16 字節(jié)
再來看
struct LGStruct3 {
double a; // 0 - 7 8字節(jié)*1
int b; // 8 - 11
char c; // 12。 8字節(jié)*2
struct LGStruct1 str1; // 24字節(jié)*1
short d; // 40 - 41
int e; // 44 -47 8字節(jié)*3
struct LGStruct2 str2; // 16字節(jié)*1
}struct3;
struct3 的內(nèi)存大小為 64 ,是不是又覺得很神奇,這里要說明的一點是,結(jié)構(gòu)體不能作為基礎(chǔ)的計算單元,根據(jù)結(jié)構(gòu)體內(nèi)部的最基礎(chǔ)的數(shù)據(jù)作為計算單元。
內(nèi)存對齊不只是基礎(chǔ)的數(shù)據(jù),在alloc中我們發(fā)現(xiàn)了一個對象內(nèi)存對齊是根據(jù)8字節(jié),但是一個對象開辟出來最低是16字節(jié),那么影響一個對象內(nèi)存大小的因素有哪些呢,對象的內(nèi)存對齊是否和結(jié)構(gòu)體一致呢。
首先創(chuàng)建一個類,這個類擁有屬性、 方法 、 變量、 協(xié)議、 分類 、擴(kuò)展
一個個排除,多測試幾次即可得知,只有變量會和屬性會影響開辟的內(nèi)存大小,而屬性沒有set和get方法本質(zhì)就是一個變量,所以根源的影響還是變量。


這里通過lldb的命令來調(diào)試
依照上面的規(guī)則,person 在設(shè)置了這7個屬性之后至少需要64個字節(jié),但是通過lldb命令得出

這里只需要48個字節(jié),那么做了哪些優(yōu)化呢

根據(jù)打印的數(shù)據(jù),我么只找到了4個屬性,那么其他的三個屬性呢,其實,這里蘋果對于內(nèi)存的優(yōu)化非常到位,內(nèi)存地址第一排的第二個地址

打印出來是錯的啊,不要著急,根據(jù)內(nèi)存對齊原則拆分一下內(nèi)存地址

拆分完即可看到,有三個屬性,age出來了,但是98和97是什么,其實98和97是ASCII表中對應(yīng)的b和a。
這樣這個對象的內(nèi)存大小就可以確定了,了解了這些,在操作底層的一些數(shù)據(jù)的時候,可以相對更優(yōu)的利用內(nèi)存。
補(bǔ)充一下內(nèi)存對齊的算法
n為對齊的總數(shù)
算法一
align 為根據(jù)幾對齊
((n + align - 1) & (~(align - 1)))
算法二
m 是 2的m次方 = 根據(jù)幾對齊
n >> m。n<< m
這兩個算法沒什么區(qū)別,主要作用是將數(shù)據(jù)的二進(jìn)制的后m位變成0