realloc函數詳解

realloc 用過很多次了。無非就是將已經存在的一塊內存擴大。

char* p = malloc(1024);
char* q = realloc(p,2048);

現在的問題是我們應該如何處理指針 p。 剛開始按照我最直觀的理解,如果就是直接將 p = NULL;。 到最後只需要釋放 q的空間就可以了。

因爲最近在做個封裝。結果在做單元測試的時候發現。有時候我在 free(q); 的時候會出錯。這樣我就鬱悶了。

後來仔細一跟蹤,發現 realloc 完以後 q 和 p 的指針地址是一樣。不過有時候又不一樣。

仔細查了下資料。得到如下信息:

       1.如果 當前連續內存塊足夠 realloc 的話,只是將p所指向的空間擴大,並返回p的指針地址。 這個時候 q 和 p 指向的地址是一樣的。

       2.如果 當前連續內存塊不夠長度,再找一個足夠長的地方,分配一塊新的內存,q,並將 p指向的內容 copy到 q,返回 q。並將p所指向的內存空間刪除。

這樣也就是說 realloc 有時候會產生一個新的內存地址 有的時候不會。所以在分配完成後。我們需要判斷下 p 是否等於 q。並做相應的處理。

這裏有點要注意的是要避免 p = realloc(p,2048); 這種寫法。有可能會造成 realloc 分配失敗後,p原先所指向的內存地址丟失。

 

=========================================

關於realloc函數說明的補充:
函數定義:
void *realloc(void *ptr, size_t size);
上面的分析基本沒有問題,但有兩點要注意:
1.返回值可能與ptr的值不同,如果是不同的話,那麼realloc函數完成後,ptr指向的舊內存已被free掉了。
2。如果返回NULL值,則分配不成功,而原來的ptr指向的內存還沒有被free掉,要求程序顯式free.

故p = (int *) realloc (p, sizeof(int) *15);語句有這麼一個問題,
調用前p指向一個已分配成功的內存,而調用realloc時卻失敗(即返回NULL),此時,p原來指向的內存還沒有free掉,而現在又找不到地址,這樣就出現memory leak了。

關於這一點的確要注意,最好如下:
int *q
q = (int *) realloc (p, sizeof(int) *15);

if(!q) p =q;

======================================================

首先看一下下面的C程序片斷:

 

#include <malloc.h>

char  *p;

p = (char * ) malloc (10);

p = (char * ) realloc (p,20);

…………………………

 

    這段程序的意思很簡單,只有稍有點C基礎的人都可以看懂。函數首先定義了一個字符型的指針p,然後爲指針p分配了一個10個字節大小的內存空間,接着將這個內存塊的大小增加到20個字節。

 

    這裏有什麼問題嗎?上機運行一下,好像沒有問題!

 

    是的,這樣上機運行是沒有問題的,但是這裏存在着也許我們不太注意的隱患!隱患在那裏?這就是我在本文中要詳細說明的realloc()函數了。

 

    再看一下下面一段來自MSDN的話:

realloc returns a void pointer to the reallocated (and possibly moved) memory block. The return value is NULL if the size is zero and the buffer argument is not NULL, or if there is not enough available memory to expand the block to the given size. In the first case, the original block is freed. In the second, the original block is unchanged. The return value points to a storage space that is guaranteed to be suitably aligned for storage of any type of object. To get a pointer to a type other than void, use a type cast on the return value.

這段E文還不算是晦澀難懂,所以我就不翻譯了,大致的意思是說關於realloc返回值的。但是這裏對他的返回值分了幾種情況:

1、  返回void * 指針,調用成功。

2、  返回NULL,當需要擴展的大小(第二個參數)爲0並且第一個參數不爲NULL,此時原內存變成了“freed(遊離)”的了。

3、  返回NULL,當沒有足夠的空間可供擴展的時候,此時,原內存空間的大小維持不變。

 

第一種情況告訴了我們在得到需要的內存空間後需要做類型轉換的工作;

第二種情況可能只有傻瓜纔會去使用吧!

第三種情況,內存空間不夠的時候就會維持未來的大小不變。

 

        MSDN上面說內存空間不夠的時候就不會擴展原來的內存空間的大小,這話固然沒有錯,但是有點含糊,似乎遺漏了一種情況!我們知道,realloc是從堆上分配內存的,當擴大一塊內存空間時, realloc()試圖直接從堆上現存的數據後面的那些字節中獲得附加的字節,如果能夠滿足,自然天下太平;可如果數據後面的字節不夠的話,問題就出來了,那麼就使用堆上第一個有足夠大小的自由塊,現存的數據然後就被拷貝至新的位置,而老塊則放回到堆上。這句話傳遞的一個重要的信息就是數據可能被移動!看到這裏,也許我們已經發現一開始我給出的程序的問題了。爲了更清楚地說明問題,可以將上面的程序改成下面的形式:

 

#include <malloc.h>

char  *p*q;

p = (char * ) malloc (10);

q=p;

p = (char * ) realloc (p,20);

…………………………

 

    這段程序也許在編譯器中沒有辦法通過,因爲編譯器可能會爲我們消除一些隱患!在這裏我們只是增加了一個記錄原來內存地址的指針q,然後記錄了原來的內存地址p,如果不幸的話,數據發生了移動,那麼所記錄的原來的內存地址q所指向的內存空間實際上已經放回到堆上了!這樣一來,我們應該終於意識到問題的所在和可怕了吧!

 

    這個問題似乎有點牛角尖的味道,因爲我們也許從來不曾遇上過,但是我們應該明白這樣的事情的始終存在,只有這樣,在萬一我們碰上的時候纔會去有意識的去避免這種隱患,否則,一旦這樣的隱患一旦發作,程序崩潰不說,恐怕查錯也不是一件容易的事!

 

    候俊傑在《深入淺出MFC》中引用林語堂的《朱門》中的一句話,我很有感觸,雖然不可能有他的感觸深,但是抱着向前輩學習的心態,所以也拿來作爲本爲的結束:

 

“只用一樣東西,不明白他的道理,實在不高明”。

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