論C語言的malloc,calloc,new,realloc,alloca的機制和區別

最近筆試老是遇到關於C語言的malloc,new之類的內存機制問題,作爲一個做java開發的程序員不免有些鬱悶,駕馭不了。乘空閒下來的這些時間,好好整理下C語言中各個內存函數的簡單機制,作用和區別:

C語言內存分配方式
  (1) 從靜態存儲區域分配。內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。例如全局變量,static變量。
  (2) 在棧上創建。在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集中,效率很高,但是分配的內存容量有限。
  (3)從堆上分配,亦稱動態內存分配。程序在運行的時候用malloc或new申請任意多少的內存,程序員自己負責在何時用free或delete釋放內存。動態內存的生存期由我們決定,使用非常靈活,但問題也最多

C語言的內存申請函數
C語言跟內存申請相關的函數主要有 alloca,calloc,malloc,free,realloc,sbrk等.
  其中alloca是向棧申請內存,因此無需釋放. malloc分配的內存是位於堆中的,並且沒有初始化內存的內容,因此基本上malloc之後,調用函數memset來初始化這部分的內存空間.
  calloc則將初始化這部分的內存,設置爲0. 而realloc則對malloc申請的內存進行大小的調整.申請的內存最終需要通過函數free來釋放. 而sbrk則是增加數據段的大小;
  malloc/calloc/free基本上都是C函數庫實現的,跟OS無關.C函數庫內部通過一定的結構來保存當前有多少可用內存.如果程序malloc的大小超出了庫裏所留存的空間,那麼將首先調用brk系統調用來增加可用空間,然後再分配空間.free時,釋放的內存並不立即返回給os,而是保留在內部結構中. 可以打個比方: brk類似於批發,一次性的向OS申請大的內存,而malloc等函數則類似於零售,滿足程序運行時的要求.這套機制類似於緩衝.

malloc的機制及與calloc的區別

malloc的底層機制是:操作系統中有一個記錄空閒內存地址的鏈表。當操作系統收到程序的申請時,就會遍歷該鏈表,然後就尋找第一個空間大於所申請空間的堆結點,然後就將該結點從空閒結點鏈表中刪除,並將該結點的空間分配給程序。

malloc()函數有一個參數,即要分配的內存空間的大小:
  Void *malloc(size_t size);

這裏,返回類型是 void* 類型。void* 表示未確定類型的指針。C,C++規定,void* 類型可以強制轉換爲任何其它類型的指針。而malloc()函數爲動態內存分配,原型:extern void *malloc(unsigned int num_bytes); 分配長度爲num_bytes字節的內存塊。如果分配成功則返回指向被分配內存的指針,分配失敗返回空指針NULL。當內存不再使用時,應使用free()函數將內存塊釋放。動態創建的對象可以用初始化變量的方式初始化:int *pi=new int(100); //指針pi所指向的對象初始化爲100,string *ps=new string(10,’9’);//*ps 爲“9999999999”

如果不提供顯示初始化,對於類類型,用該類的默認構造函數初始化;而內置類型的對象則無初始化。也可以對動態創建的對象做值初始化:int *pi=new int( );//初始化爲0,int *pi=new int;//pi 指向一個沒有初始化的int,string *ps=new string( );//初始化爲空字符串 (對於提供了默認構造函數的類類型,沒有必要對其對象進行值初始化)。

  calloc()函數有兩個參數,分別爲元素的數目和每個元素的大小,這兩個參數的乘積就是要分配的內存空間的大小:
  void*calloc(size_t numElements,size_t sizeOfElement);
    如果調用成功,函數malloc()和calloc()都將返回所分配的內存空間的首地址

malloc() 函數和calloc()函數的主要區別是前者不能初始化所分配的內存空間,而後者能。如果由malloc()函數分配的內存空間原來沒有被使用過,則其中 的每一位可能都是0;反之,如果這部分內存空間曾經被分配、釋放和重新分配,則其中可能遺留各種各樣的數據。也就是說,使用malloc()函數的程序開 始時(內存空間還沒有被重新分配)能正常運行,但經過一段時間後(內存空間已被重新分配)可能會出現問題。
  calloc() 函數會將所分配的內存空間中的每一位都初始化爲零,也就是說,如果你是爲字符類型或整數類型的元素分配內存,那麼這些元素將保證會被初始化爲零;如果你是 爲指針類型的元素分配內存,那麼這些元素通常(但無法保證)會被初始化爲空指針;如果你是爲實數類型的元素分配內存,那麼這些元素可能(只在某些計算機 中)會被初始化爲浮點型的零。
  malloc() 函數和calloc()函數的另一點區別是calloc()函數會返回一個由某種對象組成的數組,但malloc()函數只返回一個對象。
爲了明確是爲一 個數組分配內存空間,有些程序員會選用calloc()函數。但是,除了是否初始化所分配的內存空間這一點之外,絕大多數程序員認 爲以下兩種函數調用方式沒有區別:
  calloc(numElements ,sizeOfElement);
  malloc(numElements *sizeOfElement) ;

當你在calloc()函數和malloc()函數之間作選擇時,你只需考慮是否要初始化所分配的內存空間,而不用考慮函數是否能返回一個數組。

當程序運行過程中malloc了,但是沒有free的話,會造成內存泄漏.一部分的內存沒有被使用,但是由於沒有free,因此係統認爲這部分內存還在使用,造成不斷的向系統申請內存,是的系統可用內存不斷減少.但是,內存泄漏僅僅指程序在運行時,程序退出時,OS將回收所有的資源.因此,適當的重起一下程序,有時候還是有點作用.

malloc分配內存時需要注意的問題:

1)申請了內存空間後,必須檢查是否分配成功。
2)當不需要再使用申請的內存時,記得釋放;釋放後應該把指向這塊內存的指針指向NULL,防止程序後面不小心使用了它。 
3)這兩個函數應該是配對。如果申請後不釋放就是內存泄露;如果無故釋放那就是什麼也沒有做。釋放只能一次,如果釋放兩次及兩次以上會出現錯誤(釋放空指針例外,釋放空指針其實也等於啥也沒做,所以釋放空指針釋放多少次都沒有問題)。
4)雖然malloc()函數的類型是(void *),任何類型的指針都可以轉換成(void *),但是最好還是在前面進行強制類型轉換,因爲這樣可以躲過一些編譯器的檢查。

5)一旦刪除了指針所指的對象,立即將指針置爲0,這樣就非常清楚的指明指針不再指向任何對象。(零值指針:int *ip=0;)

malloc和new的區別

1)malloc與free是C++/C語言的標準庫函數,new/delete是C++的運算符。它們都可用於申請動態內存和釋放內存。

2)對於非內部數據類型的對象而言,光用maloc/free無法滿足動態對象的要求。對象在創建的同時要自動執行構造函數,對象在消亡之前要自動執行析構函數。由於malloc/free是庫函數而不是運算符,不在編譯器控制權限之內,不能夠把執行構造函數和析構函數的任務強加於malloc/free。

3)內存泄漏對於malloc或者new都可以檢查出來的,區別在於new可以指明是那個文件的那一行,而malloc沒有這些信息。

4)new可以認爲是malloc加構造函數的執行。new出來的指針是直接帶類型信息的,而malloc返回的都是void指針。

希望這篇文章能給在學C的你或者在找工作的你一些幫助微笑轉載請註明:http://blog.csdn.net/wws199304/

最後再討論下一道題:

malloc的內存free後什麼情況內存不被os回收(15騰訊筆試題)

1)來自知乎大神的回答:

若是glibc(C語言運行庫),你所free掉的內存,不一定會馬上被OS回收,這是合理的。試想一下,你每次free掉的內存都還給OS的話,尤其是在小字節的情況下,那麼造成的情況,就是一大塊的內存被你弄的千瘡百孔,也就是說一塊內存,裏面有很多gap。而在操作系統的虛擬內存管理中,更是管理着的是固定大小的內存,如4K,那你還給我1 Byte,OS顯然是很尷尬的。於是爲了避免這樣的問題,那麼內存管理一般會有一個free block list,free掉的東西就放在這裏來。那麼你可能會釋放很散亂的內存過來,沒關係,我們在這裏會嘗試合併這些散亂的block,而malloc首先找的也是free block list,而非從OS申請新的內存。那麼此時如果找到了一塊兒合適的自然最好,如果找到的是比要的更大,那麼一部分malloc,另一部分放回去。而上面有同學提到了小內存的問題,而這也是free block list在頭部會有一些所謂的administrative data,所以用標準的malloc和free管理小內存是不高效,因爲越小越容易造成gap。

簡單點講,就是free後的內存有時不會給os,而是留給程序,下次申請內存時就申請這塊內存。


      

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章