iOS-底層分析之結(jié)構(gòu)體大小計(jì)算

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

數(shù)據(jù)類型的大小

如果后面在計(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ù)怎么讀取
未內(nèi)存對(duì)齊數(shù)據(jù)存儲(chǔ)

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


未內(nèi)存對(duì)齊數(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;
內(nèi)存對(duì)齊的存儲(chǔ)

我們假設(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)存空間

上圖就是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ì)齊并不沖突

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容