摘 要:C中的malloc()函數(shù)和free()函數(shù)是一對可以動態(tài)分配內(nèi)存與釋放內(nèi)存的函數(shù),本文將從語法基礎(chǔ)、應(yīng)用場合、實現(xiàn)原理等方面對這兩個函數(shù)進行細致深入地闡述。
關(guān)鍵詞:C;malloc()函數(shù);free()函數(shù)
中圖分類號:TP312.1
1 malloc()和free()的語法基礎(chǔ)
C語言中可以用malloc()函數(shù)在內(nèi)存中動態(tài)分配一塊連續(xù)空間,分配空間大小由指定參數(shù)決定。其函數(shù)原型為:
void *malloc(unsigned int size);
返回值是所分配區(qū)域的首地址。如果內(nèi)存分配失?。ɡ鐑?nèi)存空間不足)則返回空指針NULL。如:
int *p = malloc(50); //開辟50字節(jié)的內(nèi)存空間,將空間首地址賦給指針變量p
malloc()的返回值類型為void*,很多人喜歡像下面這樣將返回值進行強制類型轉(zhuǎn)換:
int *p = (int*) malloc(50);
其實沒有必要,void*類型的指針是可以不經(jīng)強制轉(zhuǎn)換,賦給所有類型指針變量的。
如果不再需要malloc()分配的內(nèi)存空間,可以通過free()來釋放內(nèi)存。如:
free(p); //釋放 p 所指向的內(nèi)存區(qū)域
2 malloc()的應(yīng)用場合
malloc()經(jīng)常被用于分配執(zhí)行前還不確定大小的數(shù)組的內(nèi)存區(qū)域、動態(tài)分配結(jié)構(gòu)體的內(nèi)存區(qū)域等。
如在不確定書名長短時,為了避免聲明的數(shù)組長度過長造成空間浪費,可以用下列方式,在確定了書名長度后,給它分配必要的內(nèi)存區(qū)域。
char *title;
title = malloc(sizeof(char)*len); //len為書名的實際長度
再如,數(shù)據(jù)結(jié)構(gòu)中,在構(gòu)建鏈表時,要用malloc()為鏈表中的每一個結(jié)點動態(tài)分配空間。
3 malloc()和free()的實現(xiàn)原理
malloc()是在內(nèi)存中稱為“堆”的空間中進行分配的,堆的特點就是可以動態(tài)地、在運行時進行內(nèi)存分配,并且可以通過任意的順序釋放內(nèi)存。
malloc()在工作時,并不是只是獲取參數(shù)指定大小的空間,而是先一次性地獲得比較大的內(nèi)存,然后將這些內(nèi)存“零售”給應(yīng)用程序。這塊大面積的內(nèi)存空間又會被劃分成大小不一的“塊”,每個塊之前會加上一個管理區(qū)域,通過管理區(qū)域構(gòu)建一個鏈表.
通過遍歷鏈表尋找空閑的塊,如果發(fā)現(xiàn)大小合適的塊,就分割出來,將其標(biāo)記為“使用中的塊”,并將除管理區(qū)域外的空間首地址返回給應(yīng)用程序。如果不存在合適的空塊,malloc()就會請求操作系統(tǒng)對空間進行擴容。free()將指定位置的管理區(qū)域的標(biāo)記改寫成“空塊”,如果其上下剛好也是空快,free()就會將這些塊合并成一個整塊,這樣可以防止塊的碎片化。
在這種內(nèi)存運行環(huán)境下,一旦數(shù)組產(chǎn)生越界,在malloc()分配的空間之外寫入了數(shù)據(jù),就會破壞下一個塊的管理區(qū)域,在以后的 malloc()和 free()調(diào)用中,出現(xiàn)程序崩潰的可能性會非常大。
當(dāng)然,以上只是malloc()功能最單純的一種實現(xiàn)方式,現(xiàn)實環(huán)境中,是不會這樣的。比如,除了這里介紹的鏈表實現(xiàn)方式之外,“buddy system” 也是一個被大家廣泛熟知的內(nèi)存管理方法。這種方式將大的內(nèi)存逐步分成兩個大小相等的小塊,速度很快,但缺點是會造成內(nèi)存使用效率的降低。
此外,讓管理區(qū)域和實際可用區(qū)域相鄰也是比較危險的,所以有的實現(xiàn)中會將它們分開存放。
4 free()探秘
如前所述,malloc()在工作時會一次性地獲得比較大的內(nèi)存,然后將這些內(nèi)存“零售”給應(yīng)用程序。因此,一般來說調(diào)用 free()之后,對應(yīng)的內(nèi)存區(qū)域也是不會立即返還給操作系統(tǒng)的??梢酝ㄟ^以下程序來做個實驗。
#include
#include
int main(void)
{
int * p;
p = malloc(sizeof(int));
*p = 123456;
free(p);
printf(\"%d\n\", *p);
return 0;
}
在某些環(huán)境(如UNIX系統(tǒng))下執(zhí)行這個程序,依然會輸出123456。之后隨著某次malloc()調(diào)用,恰好將這片區(qū)域重新進行分配后,這部分內(nèi)容才會被改寫。因此,調(diào)用 free()之后,是不能立即引用其釋放的內(nèi)存區(qū)域的。
這樣的特性往往會給程序帶來難以查明的bug。比如有兩個指針正同時引用某內(nèi)存區(qū)域。使用指針1的程序員已經(jīng)不需要這片空間了,于是調(diào)用了free()函數(shù)。實際上,在程序的另一處,指針2還在引用當(dāng)前這塊空間,這塊空間并沒有立即被破壞,在空間內(nèi)還保持著以前的值。直到在程序的某處又執(zhí)行了malloc(),這塊內(nèi)存空間被重新分配,其中的內(nèi)容才可能被破壞。這樣的程序漏洞比較隱蔽,從產(chǎn)生到被發(fā)現(xiàn)之間,可能會經(jīng)歷比較長的時間,因此會給程序調(diào)式、糾錯帶來很大困難。
這種情況在大型程序中常常會碰到,為了避免這個問題,可把free()放在另一個自定義的函數(shù)里,并且使程序員只能調(diào)用這個自定義的函數(shù)。在該函數(shù)中,可以在釋放空間之前故意將它破壞。如果想要知道當(dāng)前指針指向的區(qū)域的大小,還可以考慮將 malloc()也寫在自定義函數(shù)中,每次分配內(nèi)存的時候可以多留出一點空間,將區(qū)域的大小信息寫在最前面的部分。
5 結(jié)束語
本文詳細闡述了malloc()函數(shù)和free()函數(shù)在語法基礎(chǔ)、應(yīng)用場合、實現(xiàn)原理等方面的知識。深入了解這兩個函數(shù),可以幫助我們更好地了解系統(tǒng)內(nèi)存的使用情況,并能減少編程中可能由此引起的隱蔽的錯誤。
參考文獻:
[1]譚浩強.C語言程序設(shè)計[M].清華大學(xué)出版社,2010.
[2]前橋和彌.征服C指針[M].人民郵電出版社,2013.
[3]賀偉.malloc函數(shù)在Linux系統(tǒng)下的原理性實現(xiàn)[J].福建電腦,2010(6).
[4]戴春燕,徐智文.對C++中malloc/free和new/delete的探討[J].包鋼科技,2009,35(01).
[5]徐翔.c++程序設(shè)計教學(xué)探討[J].硅谷,2009(02).
[6]安晶,謝瑩.c++教學(xué)探討[J].黑龍江科技信息,2009(03).
[7]高燕燕,王崗.淺談C++/C中內(nèi)存的申請和釋放[J].中國高新技術(shù)企業(yè),2008(22).
[8]Melody的專欄.Buddy System[EB/OL].http://blog.csdn.net/zhangqingsup/article/details/6661638.
作者簡介:陸金江(1982-),女,江蘇宿遷人,專職教師,講師,工學(xué)學(xué)士,研究方向:軟件技術(shù)。
作者單位:安徽財貿(mào)職業(yè)學(xué)院,合肥 230601