開始之前,我們先放上一張不同的數(shù)據(jù)類型所占用的大小的表,免的后面再計(jì)算的時(shí)候有些懵逼

如果后面在計(jì)算內(nèi)存占用的時(shí)候不清楚,請(qǐng)翻到這里查看。下面的例子里面都是在64位機(jī)器上運(yùn)行
這里是分割線,下面的內(nèi)容純屬裝逼~~~
例1
我們先定義一個(gè)結(jié)構(gòu)體:
struct WStruct {
double a;
int b;
char c;
short d;
}WStruct1;
我們知道了這個(gè)結(jié)構(gòu)體每個(gè)元素所占用的內(nèi)存空間如下:
| Double | 8 |
|---|---|
| Int | 4 |
| Char | 1 |
| Short | 2 |
這樣我們計(jì)算出WStruct1結(jié)構(gòu)體所占用的內(nèi)存應(yīng)該為15字節(jié),但是我們使用sizeof打印一下看看結(jié)果:
NSLog(@"%lu",sizeof(WStruct1));
001-內(nèi)存對(duì)齊原則[75942:873202] 16
打印出的結(jié)果為16,這是為什么呢?
這是因?yàn)橄到y(tǒng)自動(dòng)做了
內(nèi)存對(duì)齊
為什么要內(nèi)存對(duì)齊,以及系統(tǒng)怎么做的字節(jié)對(duì)齊,是接下來(lái)我們要研究的東西
為什么要內(nèi)存對(duì)齊
要搞清楚這個(gè)問(wèn)題,我們要清楚
- 如果不內(nèi)存對(duì)齊,數(shù)據(jù)是怎么存儲(chǔ)
- 如果不內(nèi)存對(duì)齊,數(shù)據(jù)怎么讀取

我們?cè)诳纯催@樣存儲(chǔ)以后,數(shù)據(jù)是怎么讀取的:

如果不做內(nèi)存對(duì)齊,那么在讀取的時(shí)候就需要不停的去變換讀取的長(zhǎng)度,這是非常消耗性能的,會(huì)大大降低讀取效率
那么既然我們知道了數(shù)據(jù)在存儲(chǔ)的時(shí)候會(huì)進(jìn)行內(nèi)存對(duì)齊,那么內(nèi)存對(duì)齊是按照什么規(guī)則進(jìn)行的呢?
內(nèi)存對(duì)齊的規(guī)則
1、數(shù)據(jù)成員對(duì)?規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員,第一個(gè)數(shù)據(jù)成員放在offset為0的地方,以后每個(gè)數(shù)據(jù)成員存儲(chǔ)的起始位置要從該成員大小或者成員的子成員大小(只要該成員有子成員,比如說(shuō)是數(shù)組,結(jié)構(gòu)體等)的整數(shù)倍開始(比如int為4字節(jié),則要從4的整數(shù)倍地址開始存儲(chǔ))。
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ǔ)?
以上就是內(nèi)存對(duì)齊的文字描述,接下來(lái)我們還是結(jié)合上面結(jié)構(gòu)體來(lái)驗(yàn)證一下:
struct WStruct {
double a;
int b;
char c;
short d;
}WStruct1;

我們假設(shè)從位置0開始存儲(chǔ):
double占用8個(gè)字節(jié),所以占用0-7位置的內(nèi)存空間
由于0-7已經(jīng)被double占用,在存儲(chǔ)int的時(shí)候至少要從8號(hào)位置開始,我們知道int占用4個(gè)字節(jié),8正好是4的整數(shù)倍所以int占用8-11的內(nèi)存空間
接下來(lái)存儲(chǔ)char,char占用1個(gè)字節(jié),所以直接放在12內(nèi)存地址
short占用2個(gè)字節(jié)的內(nèi)存,但是開始存儲(chǔ)的位置13并不是2的整數(shù)倍,所以我們需要將short的存儲(chǔ)位置后移一個(gè)字節(jié),從14開始存儲(chǔ),占用14-15內(nèi)存空間
通過(guò)這種方式,WStruct1結(jié)構(gòu)體就會(huì)占用16個(gè)字節(jié)的內(nèi)存空間。
內(nèi)存對(duì)齊的讀取
進(jìn)行內(nèi)存對(duì)齊后,我們?cè)谧x取這個(gè)結(jié)構(gòu)體的時(shí)候,會(huì)按照結(jié)構(gòu)體中占用最大內(nèi)存屬性的整數(shù)倍進(jìn)行讀取,也就是說(shuō),WStruct1通過(guò)兩次讀取就可以讀取出所有的數(shù)據(jù)
那么接下來(lái)我們通過(guò)一個(gè)復(fù)雜的例子來(lái)計(jì)算一下所占用的內(nèi)存空間:
例子2
struct WStruct2 {
double a;
char b;
int c;
short d;
struct WStruct e;
}WStruct2;
我們看到這個(gè)例子中包含了一個(gè)結(jié)構(gòu)體屬性,那么這樣的結(jié)構(gòu)體需要占用多少內(nèi)存空間呢?

上圖就是WStruct2所占用的內(nèi)存空間,我們看打印結(jié)果
001-內(nèi)存對(duì)齊原則[77470:954388] 40
也是40個(gè)字節(jié)
總結(jié)
內(nèi)存對(duì)齊實(shí)際上是犧牲了一些內(nèi)存空間來(lái)讓內(nèi)存數(shù)據(jù)的讀取更加快速,是一種空間換時(shí)間的過(guò)程。我們上一章節(jié)說(shuō)明了再iOS系統(tǒng)中,普遍采用16字節(jié)對(duì)齊的方式來(lái)進(jìn)行存儲(chǔ),本篇講的是具體數(shù)據(jù)類型的存儲(chǔ),16字節(jié)對(duì)齊主要針對(duì)對(duì)象的存儲(chǔ),和本篇所講的內(nèi)存對(duì)齊并不沖突