關(guān)于C語(yǔ)言中Malloc()內(nèi)存對(duì)齊
眾所周知,在C語(yǔ)言中可以通過(guò)malloc()函數(shù)在堆區(qū)創(chuàng)建一塊動(dòng)態(tài)內(nèi)存空間,例如:
malloc(1);? //在堆區(qū)創(chuàng)建1個(gè)字節(jié)大小的空間
但是實(shí)際情況下malloc()創(chuàng)建的數(shù)據(jù)大小遠(yuǎn)超過(guò)1字節(jié),為什么會(huì)導(dǎo)致這個(gè)問(wèn)題呢,這就有關(guān)內(nèi)存對(duì)齊問(wèn)題了。
1.實(shí)驗(yàn)
我分別在Win10 64下和Stm32下運(yùn)行了如下代碼,并得到結(jié)果:


先觀察運(yùn)行結(jié)果,Stm32分別輸出了8和16,明明我們分配的是malloc(1),應(yīng)該輸出1字節(jié)才對(duì)啊,怎么會(huì)變成8,更不要用說(shuō)16了。
這就要引入內(nèi)存對(duì)齊和malloc堆塊結(jié)構(gòu)的的問(wèn)題了。
首先我們先看malloc()堆塊結(jié)構(gòu),malloc()函數(shù)申請(qǐng)的堆塊是由兩部分組成:Header和數(shù)據(jù)區(qū)

malloc()函數(shù)創(chuàng)建的堆空間由兩部分組成:Header和數(shù)據(jù),這就意味著malloc()真實(shí)分配的空間大小是Header大小+數(shù)據(jù)大小。
以下代碼為malloc()堆塊Header結(jié)構(gòu):
typedef?long?Align;
union?header {
????struct?{
????????union?header *ptr;
????????unsigned?
????} s;
????Align x;
};
可以看到Header是共同體Union,共同體所有元素共享一塊內(nèi)存空間,共同體所占用的空間大小是共同體中最大的元素的大小,也就是long Align x,眾所周知long在64位機(jī)子占用8字節(jié),在32位機(jī)子占用4字節(jié)。
Header長(zhǎng)度是4字節(jié)或8字節(jié)(根據(jù)機(jī)器字長(zhǎng)決定)
注意:我經(jīng)過(guò)Stm32平臺(tái)和Win10平臺(tái)實(shí)驗(yàn)論證,Header區(qū)和數(shù)據(jù)區(qū)是一個(gè)整體,計(jì)算實(shí)際內(nèi)存占用時(shí)先把數(shù)據(jù)區(qū)和Header長(zhǎng)度相加,然后把相加后的結(jié)果作內(nèi)存對(duì)齊。以32位機(jī)子的malloc(1)為例,把數(shù)據(jù)區(qū)1字節(jié)加上Header的4字節(jié)得到總共5字節(jié),再整體作內(nèi)存對(duì)齊得到8字節(jié),實(shí)際內(nèi)存占用為8字節(jié)。
再說(shuō)內(nèi)存對(duì)齊問(wèn)題
比如此處我用Stm32申請(qǐng)了1個(gè)字節(jié)的堆空間,實(shí)際上他卻分配了8個(gè)字節(jié)給我,按照malloc()函數(shù)分配的堆塊結(jié)構(gòu),應(yīng)該是1個(gè)字節(jié)的數(shù)據(jù)區(qū)+4個(gè)字節(jié)的Header區(qū),再經(jīng)過(guò)內(nèi)存對(duì)齊,小于8字節(jié)的區(qū)域擴(kuò)展到8字節(jié)存儲(chǔ),進(jìn)行內(nèi)存對(duì)齊后我們總共占用8字節(jié)。
經(jīng)過(guò)測(cè)試,在不同的編譯環(huán)境和平臺(tái)下,系統(tǒng)默認(rèn)的內(nèi)存對(duì)齊系數(shù)是不一致的,32位數(shù)系統(tǒng)是8字節(jié)(少于8字節(jié)則以8字節(jié)存儲(chǔ),大于8字節(jié)則擴(kuò)展8字節(jié)存儲(chǔ)),64位系統(tǒng)是16字節(jié)(少于16字節(jié)則以16字節(jié)存儲(chǔ),大于16字節(jié)則擴(kuò)展16字節(jié)存儲(chǔ))
Stm32經(jīng)過(guò)測(cè)試,對(duì)齊系數(shù)是8字節(jié),但是注意,經(jīng)過(guò)我的實(shí)驗(yàn),在Header區(qū)4字節(jié)加上數(shù)據(jù)區(qū)大小不產(chǎn)生溢出時(shí)(兩者相加不超過(guò)最近的8的倍數(shù)),對(duì)齊系數(shù)會(huì)在8字節(jié)和16字節(jié)間交替,產(chǎn)生溢出時(shí)就是正常的8字節(jié)對(duì)齊,這就是為什么上面測(cè)試會(huì)出現(xiàn)8和16交替的情況。當(dāng)malloc()申請(qǐng)1字節(jié)時(shí),數(shù)據(jù)區(qū)長(zhǎng)度1字節(jié)加上Header區(qū)長(zhǎng)度4字節(jié)經(jīng)過(guò)內(nèi)存對(duì)齊后變成8字節(jié),總長(zhǎng)度=(數(shù)據(jù)區(qū)1字節(jié)+Header區(qū)4字節(jié))->內(nèi)存對(duì)齊=8字節(jié)
經(jīng)過(guò)實(shí)驗(yàn),Stm32F103C8的內(nèi)存對(duì)齊系數(shù)是8字節(jié)和16字節(jié)交替的,當(dāng)數(shù)據(jù)區(qū)長(zhǎng)度加Header長(zhǎng)度產(chǎn)生溢出時(shí),內(nèi)存對(duì)齊系數(shù)就是正常的8字節(jié),當(dāng)數(shù)據(jù)區(qū)長(zhǎng)度加Header長(zhǎng)度不產(chǎn)生溢出時(shí),內(nèi)存對(duì)齊系數(shù)就會(huì)交替。例如malloc(1),數(shù)據(jù)區(qū)長(zhǎng)度是1字節(jié),加上4后小于8不產(chǎn)生溢出,則內(nèi)存對(duì)齊系數(shù)就會(huì)在8和16之間交替,再例如malloc(7),7+4大于8產(chǎn)生溢出,那么內(nèi)存對(duì)齊系數(shù)就是8字節(jié),總占用16字節(jié)。使用時(shí)需注意,這也就解釋了為什么上面示例中一會(huì)輸出8字節(jié)一會(huì)輸出16字節(jié)了
Win10 64經(jīng)過(guò)測(cè)試,對(duì)齊系數(shù)是16字節(jié),當(dāng)malloc()申請(qǐng)32字節(jié)時(shí),實(shí)際我們需要的是32字節(jié)數(shù)據(jù)區(qū)空間+8字節(jié)Header空間,也就是40字節(jié),數(shù)據(jù)區(qū)和Header區(qū)相加后內(nèi)存對(duì)齊,系統(tǒng)會(huì)分配48字節(jié)的空間給我們存儲(chǔ),如下圖:

系統(tǒng)最小malloc()分配空間大小
在64位系統(tǒng),如果申請(qǐng)內(nèi)存為1~24字節(jié),系統(tǒng)內(nèi)存消耗32字節(jié),當(dāng)申請(qǐng)25字節(jié)的內(nèi)存時(shí),系統(tǒng)內(nèi)存消耗48字節(jié)。而對(duì)于32位系統(tǒng),申請(qǐng)內(nèi)存為1~12字節(jié)時(shí),系統(tǒng)內(nèi)存消耗為16字節(jié),當(dāng)申請(qǐng)內(nèi)存為13字節(jié)時(shí),系統(tǒng)內(nèi)存消耗為24字節(jié)
總結(jié)
malloc()函數(shù)創(chuàng)建的實(shí)際空間大小=系統(tǒng)最小分配空間大小+超出部分內(nèi)存對(duì)齊后的值
注意:下例Stm32內(nèi)存對(duì)齊系數(shù)以8字節(jié)為準(zhǔn)
例如:
malloc(1)
在Win10 64下實(shí)際創(chuàng)建(1+8)=系統(tǒng)最小分配空間32+超出部分0=32字節(jié)
在Stm32下實(shí)際創(chuàng)建(1+8)=系統(tǒng)最小分配空間8+(超出部分1->內(nèi)存對(duì)齊后8)=16字節(jié)
再例如:
malloc(32)
在Win10?64下實(shí)際創(chuàng)建(32+8)=系統(tǒng)最小分配空間32+(超出部分8->內(nèi)存對(duì)齊后16)=48字節(jié)
在Stm32下實(shí)際創(chuàng)建(32+8)=系統(tǒng)最小分配空間8+(超出部分32->內(nèi)存對(duì)齊后32)=40字節(jié)
再例如:
malloc(47)
在Win10?64下實(shí)際創(chuàng)建(47+8)=系統(tǒng)最小分配空間32+(超出部分23->內(nèi)存對(duì)齊后32)=64字節(jié)
在Stm32下實(shí)際創(chuàng)建(47+8)=系統(tǒng)最小分配空間8+(超出部分47->內(nèi)存對(duì)齊后48)=56字節(jié)


相信很多帶佬不用看我上面的廢話,直接看例子就能看懂了。