new與malloc的區別,以及內存分配淺析

 
從函數聲明上能夠看出。malloc 和 new 至少有兩個不一樣: new 返回指定類型的 指針,而且能夠自動計算所須要大小。好比:
1
2
3
int *p;
p = new int ;
//返回類型爲int* 類型(整數型指針),分配大小爲 sizeof(int);
或:
1
2
3
int * parr;
parr = new int [100];
//返回類型爲 int* 類型(整數型指針),分配大小爲 sizeof(int) * 100;
而 malloc 則必需要由咱們計算字節數,而且在返回後強行轉換爲實際類型的 指針
1
2
3
4
5
6
7
int * p;
p = ( int *) malloc ( sizeof ( int )*128);
//分配128個(可根據實際須要替換該數值)整型存儲單元,
//並將這128個連續的整型存儲單元的首地址存儲到指針變量p中
double *pd=( double *) malloc ( sizeof ( double )*12);
//分配12個double型存儲單元,
//並將首地址存儲到指針變量pd中
第1、malloc 函數返回的是 void * 類型。對於C++,若是你寫成:p = malloc (sizeof(int)); 則程序沒法經過編譯,報錯:「不能將 void* 賦值給 int * 類型 變量」。因此必須經過 (int *) 來將 強制轉換。而對於C,沒有這個要求,但爲了使C程序更方便的移植到C++中來,建議養成 強制轉換的習慣。
第2、函數的 實參爲 sizeof(int) ,用於指明一個 整型數據須要的大小。若是你寫成:
1
int * p = ( int *) malloc (1);
代碼也能經過編譯,但事實上只分配了1個字節大小的內存空間,當你往裏頭存入一個整數,就會有3個字節無家可歸,而直接「住進鄰居家」!形成的結果是後面的內存中原有數據內容被改寫。
 
下面一段話的原理講的比較清晰
malloc函數的實質體如今,它有一個將可用的內存塊鏈接爲一個長長的列表的所謂空閒 鏈表。調用malloc函數時,它沿 鏈接表尋找一個大到足以知足用戶請求所須要的內存塊。而後,將該內存塊一分爲二(一塊的大小與用戶請求的大小相等,另外一塊的大小就是剩下的字節)。接下來,將分配給用戶的那塊內存傳給用戶,並將剩下的那塊(若是有的話)返回到鏈接表上。調用 free函數時,它將用戶釋放的內存塊鏈接到空閒鏈上。到最後,空閒鏈會被切成不少的小內存片斷,若是這時用戶申請一個大的內存片斷,那麼空閒鏈上可能沒有能夠知足用戶要求的片斷了。因而,malloc函數請求延時,並開始在空閒鏈上翻箱倒櫃地檢查各內存片斷,對它們進行整理,將相鄰的小空閒塊合併成較大的內存塊。若是沒法得到符合要求的內存塊,malloc函數會返回NULL 指針,所以在調用malloc動態申請內存塊時,必定要進行返回值的判斷。
 

2、malloc()到底從哪裏得來了內存空間:html

一、malloc()到底從哪裏獲得了內存空間?答案是從堆裏面得到空間。也就是說函數返回的指針是指向堆裏面的一塊內存。操做系統中有一個記錄空閒內存地址的鏈表。當操做系統收到程序的申請時,就會遍歷該鏈表,而後就尋找第一個空間大於所申請空間的堆結點,而後就將該結點從空閒結點鏈表中刪除,並將該結點的空間分配給程序。就是這樣!程序員

   說到這裏,不得不另外插入一個小話題,相信你們也知道是什麼話題了。什麼是堆?說到堆,又忍不住說到了棧!什麼是棧?下面就另外開個小部分專門而又簡單地說一下這個題外話:編程

二、什麼是堆:堆是你們共有的空間,分全局堆和局部堆。全局堆就是全部沒有分配的空間,局部堆就是用戶分配的空間。堆在操做系統對進程 初始化的時候分配,運行過程當中也能夠向系統要額外的堆,可是記得用完了要還給操做系統,要否則就是內存泄漏。數據結構

   什麼是棧:棧是線程獨有的,保存其運行狀態和局部自動變量的。棧在線程開始的時候初始化,每一個線程的棧互相獨立。每一個函數都有本身的棧,棧被用來在函數之間傳遞參數。操做系統在切換線程的時候會自動的切換棧,就是切換SS/ESP寄存器。棧空間不須要在高級語言裏面顯式的分配和釋放。函數

   以上的概念描述是標準的描述,不過有個別語句被我刪除,不知道由於這樣而變得不標準了^_^.spa

   經過上面對概念的描述,能夠知道:操作系統

   棧是由編譯器自動分配釋放,存放函數的參數值、局部變量的值等。操做方式相似於數據結構中的棧。.net

   堆通常由程序員分配釋放,若不釋放,程序結束時可能由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()究竟是怎麼分配空間的,這裏面涉及到不少其餘方面的知識!有興趣的朋友能夠本身去下載源
代碼去看看。

//部分轉載自http://www.bccn.net/Article/kfyy/cyy/jszl/200608/4238_2.html 

相關文章
相關標籤/搜索