原文:http://blog.pfan.cn/vfdff/33507.html
html
我的總結程序員
在C語言的學習中,對內存管理這部分的知識掌 握尤爲重要!以前對C中的malloc()和free()兩個函數的瞭解甚少,只知道大概該怎麼用—— 就是malloc而後free就一切OK了。固然如今對這兩個函數的體會也不見得多,不過對於本文章第三部分的內容卻是有了轉折性的認識,因此
寫下這篇文章做爲一個對知識的總結。這篇文章之因此命名中有個「淺談」的字眼,也就是這個意思了!但願對你們有一點幫助!編程
若是不扯得太遠的話(好比說操做系統中虛擬內存和物理內存如何運作如何管理之類的知識等),我感受這篇文章應該是比較全面地談了一下malloc()和free().這篇文章由淺入深(不見得有多深)分三個部分介紹主要內容。數據結構
廢話了那麼多,下面馬上進入主題================》》》》》》》》》》》》》》》》》》》》》》》》》》》》》函數
1、malloc()和free()的基本概念以及基本用法:學習
一、函數原型及說明:spa
void *malloc(long NumBytes):該函數分配了NumBytes個字節,並返回了指向這塊內存的指針。若是分配失敗,則返回一個空指針(NULL)。操作系統
關於分配失敗的緣由,應該有多種,好比說空間不足就是一種。.net
void free(void *FirstByte): 該函數是將以前用malloc分配的空間還給程序或者是操做系統,也就是釋放了這塊內存,讓它從新獲得自由。線程
二、函數的用法:
其實這兩個函數用起來倒不是很難,也就是malloc()以後以爲用夠了就甩了它把它給free()了,舉個簡單例子:
// Code...
char *Ptr = NULL;
Ptr = (char *)malloc(100 * sizeof(char));
if (NULL == Ptr)
{
exit (1);
}
gets(Ptr);
// code...
free(Ptr);
Ptr = NULL;
// code...
就是這樣!固然,具體狀況要具體分析以及具體解決。好比說,你定義了一個指針,在一個函數裏申請了一塊內存而後經過函數返回傳遞給這個指針,那麼也許釋放這塊內存這項工做就應該留給其餘函數了。
三、關於函數使用須要注意的一些地方:
A、申請了內存空間後,必須檢查是否分配成功。
B、當不須要再使用申請的內存時,記得釋放;釋放後應該把指向這塊內存的指針指向NULL,防止程序後面不當心使用了它。
C、這兩個函數應該是配對。若是申請後不釋放就是內存泄露;若是無端釋放那就是什麼也沒有作。釋放只能一次,若是釋放兩次及兩次以上會
出現錯誤(釋放空指針例外,釋放空指針其實也等於啥也沒作,因此釋放空指針釋放多少次都沒有問題)。
D、雖然malloc()函數的類型是(void *),任何類型的指針均可以轉換成(void *),可是最好仍是在前面進行強制類型轉換,由於這樣能夠躲過一
些編譯器的檢查。
2、malloc()到底從哪裏得來了內存空間:
一、malloc()到底從哪裏獲得了內存空間?答案是從堆裏面得到空間。也就是說函數返回的指針是指向堆裏面的一塊內存。操做系統中有一個記錄空 閒內存地址的鏈表。當操做系統收到程序的申請時,就會遍歷該鏈表,而後就尋找第一個空間大於所申請空間的堆結點,而後就將該結點從空閒結點鏈表中刪除,並 將該結點的空間分配給程序。就是這樣!
說到這裏,不得不另外插入一個小話題,相信你們也知道是什麼話題了。什麼是堆?說到堆,又忍不住說到了棧!什麼是棧?下面就另外開個小部分專門而又簡單地說一下這個題外話:
二、什麼是堆:堆是你們共有的空間,分全局堆和局部堆。全局堆就是全部沒有分配的空間,局部堆就是用戶分配的空間。堆在操做系統對進程 初始化的時候分配,運行過程當中也能夠向系統要額外的堆,可是記得用完了要還給操做系統,要否則就是內存泄漏。
什麼是棧:棧是線程獨有的,保存其運行狀態和局部自動變量的。棧在線程開始的時候初始化,每一個線程的棧互相獨立。每一個函數都有本身的棧,棧被用來在函數之 間傳遞參數。操做系統在切換線程的時候會自動的切換棧,就是切換SS/ESP寄存器。棧空間不須要在高級語言裏面顯式的分配和釋放。
以上的概念描述是標準的描述,不過有個別語句被我刪除,不知道由於這樣而變得不標準了^_^.
經過上面對概念的描述,能夠知道:
棧是由編譯器自動分配釋放,存放函數的參數值、局部變量的值等。操做方式相似於數據結構中的棧。
堆通常由程序員分配釋放,若不釋放,程序結束時可能由OS回收。注意這裏說是可能,並不是必定。因此我想再強調一次,記得要釋放!
注意它與數據結構中的堆是兩回事,分配方式卻是相似於鏈表。(這點我上面稍微提過)
因此,舉個例子,若是你在函數上面定義了一個指針變量,而後在這個函數裏申請了一塊內存讓指針指向它。實際上,這個指針的地址是在棧上,可是它所指向的內容倒是在堆上面的!這一點要注意!因此,再想一想,在一個函數裏申請了空間後,好比說下面這個函數:
// code...
void Function(void)
{
char *p = (char *)malloc(100 * sizeof(char));
}
就這個例子,千萬不要認爲函數返回,函數所在的棧被銷燬指針也跟着銷燬,申請的內存也就同樣跟着銷燬了!這絕對是錯誤的!由於申請的內存在堆上,而函數所在的棧被銷燬跟堆徹底沒有啥關係。因此,仍是那句話:記得釋放!
三、free()到底釋放了什麼
這個問題比較簡單,其實我是想和第二大部分的題目相呼應而已!哈哈!free()釋放的是指針指向的內存!注意!釋放的是內存,不是指針!這點很是很是重 要!指針是一個變量,只有程序結束時才被銷燬。釋放了內存空間後,原來指向這塊空間的指針仍是存在!只不過如今指針指向的內容的垃圾,是未定義的,因此說 是垃圾。所以,前面我已經說過了,釋放內存後把指針指向NULL,防止指針在後面不當心又被解引用了。很是重要啊這一點!
好了!這個「題外話」終於說完了。就這麼簡單說一次,知道個大概就能夠了!下面就進入第三個部分:
3、malloc()以及free()的機制:
這個部分我今天才有了新的認識!並且是轉折性的認識!因此,這部分可能會有更多一些認識上的錯誤!不對的地方請你們幫忙指出!
事實上,仔細看一下free()的函數原型,也許也會發現彷佛很神奇,free()函數很是簡單,只有一個參數,只要把指向申請空間的指針傳遞
給free()中的參數就能夠完成釋放工做!這裏要追蹤到malloc()的申請問題了。申請的時候實際上佔用的內存要比申請的大。由於超出的空間是用來記錄對這塊內存的管理信息。先看一下在《UNIX環境高級編程》中第七章的一段話:
大多數實現所分配的存儲空間比所要求的要稍大一些,額外的空間用來記錄管理信息——分配塊的長度,指向下一個分配塊的指針等等。這就意味着若是寫過一個已 分配區的尾端,則會改寫後一塊的管理信息。這種類型的錯誤是災難性的,可是由於這種錯誤不會很快就暴露出來,因此也就很難發現。將指向分配塊的指針向後移 動也可能會改寫本塊的管理信息。
以上這段話已經給了咱們一些信息了。malloc()申請的空間實際我以爲就是分了兩個不一樣性質的空間。一個就是用來記錄管理信息的空間,另一個就是可用空間了。而用來記錄管理信息的其實是一個結構體。在C語言中,用結構體來記錄同一個對象的不一樣信息是
天經地義的事!下面看看這個結構體的原型:
struct mem_control_block {
int is_available; //這是一個標記?
int size; //這是實際空間的大小
};
對於size,這個是實際空間大小。這裏其實我有個疑問,is_available是不是一個標記?由於我看了free()的源代碼以後對這個變量感受有點納悶(源代碼在下面分析)。這裏還請你們指出!
因此,free()就是根據這個結構體的信息來釋放malloc()申請的空間!而結構體的兩個成員的大小我想應該是操做系統的事了。可是這裏有一個問 題,malloc()申請空間後返回一個指針應該是指向第二種空間,也就是可用空間!否則,若是指向管理信息空間的話,寫入的內容和結構體的類型有可能不 一致,或者會把管理信息屏蔽掉,那就無法釋放內存空間了,因此會發生錯誤!(感受本身這裏說的是廢話)
好了!下面看看free()的源代碼,我本身分析了一下,以爲比起malloc()的源代碼卻是容易簡單不少。只是有個疑問,下面指出!
// code...
void free(void *ptr)
{
struct mem_control_block *free;
free = ptr - sizeof(struct mem_control_block);
free->is_available = 1;
return;
}
看一下函數第二句,這句很是重要和關鍵。其實這句就是把指向可用空間的指針倒回去,讓它指向管理信息的那塊空間,由於這裏是在值上減去了一個結構體的大 小!後面那一句free->is_available = 1;我有點納悶!個人想法是:這裏is_available應該只是一個標記而已!由於從這個變量的名稱上來看,is_available 翻譯過來就是「是能夠用」。不要說我土!我以爲變量名字能夠反映一個變量的做用,特別是嚴謹的代碼。這是源代碼,因此我以爲絕對是嚴謹的!!這個變量的值 是1,代表是能夠用的空間!只是這裏我想了想,若是把它改成0或者是其餘值不知道會發生什麼事?!可是有一點我能夠確定,就是釋放絕對不會那麼順利進行! 由於這是一個標記!
固然,這裏可能仍是有人會有疑問,爲何這樣就能夠釋放呢??我剛纔也有這個疑問。後來我想到,釋放是操做系統的事,那麼就free()這個源代碼來看, 什麼也沒有釋放,對吧?可是它確實是肯定了管理信息的那塊內存的內容。因此,free()只是記錄了一些信息,而後告訴操做系統那塊內存能夠去釋放,具體 怎麼告訴操做系統的我不清楚,但我以爲這個已經超出了我這篇文章的討論範圍了。
那麼,我以前有個錯誤的認識,就是認爲指向那塊內存的指針無論移到那塊內存中的哪一個位置均可以釋放那塊內存!可是,這是大錯特錯!釋放是不能夠釋放一部分 的!首先這點應該要明白。並且,從free()的源代碼看,ptr只能指向可用空間的首地址,否則,減去結構體大小以後必定不是指向管理信息空間的首地 址。因此,要確保指針指向可用空間的首地址!不信嗎?本身能夠寫一個程序而後移動指向可用空間的指針,看程序會有會崩!
最後可能想到malloc()的源代碼看看malloc()究竟是怎麼分配空間的,這裏面涉及到不少其餘方面的知識!有興趣的朋友能夠本身去下載源
代碼去看看。
4、關於其餘:
關於C中的malloc()和free()的討論就寫到這裏吧!寫了三個鐘頭,感受有點累!但願對你們有所幫助!有不對的地方歡迎你們指出!最後
,謝謝參與這個帖子討論的全部朋友,帖子:http://www.bc-cn.net/bbs/dispbbs.asp?boardID=5&ID=81781&page=1。也談到版權問題,若是哪位想轉載這篇文章(若是我有這個榮幸的話),最起碼請標明「來自bc-cn C語言論壇」這幾個字眼,個人ID能夠不用寫上!謝謝合做!
5、參考文獻:(只寫書名)
——《UNIX環境高級編程》
——《計算機組成原理》
——《高質量C/C++編程指南》