1. 程式人生 > >realloc不安全函式

realloc不安全函式

C語言 realloc() 函式位於 stdlib.h 標頭檔案中,其原型為:
    void *realloc(void *ptr, size_t size);

realloc() 會將 ptr 所指向的記憶體塊的大小修改為 size,並將新的記憶體指標返回。

設之前記憶體塊的大小為 n,如果 size < n,那麼擷取的內容不會發生變化,如果 size > n,那麼新分配的記憶體不會被初始化。

如果 ptr = NULL,那麼相當於呼叫 malloc(size);如果 size = 0,那麼相當於呼叫 free(ptr)。

如果 ptr 不為 NULL,那麼他肯定是由之前的記憶體分配函式返回的,例如 malloc()、calloc()或realloc()。


如果 ptr 所指的記憶體塊被移動,那麼會呼叫 free(ptr)。

看吧,一個簡單的 realloc() 卻賦予了好幾個功能,這並不是良好的函式設計。估計也是為了相容性,才容忍這個函式一直在C庫中。雖然在編碼中,realloc() 會提供一定的方便,但是也很容易引發Bug。

下面就舉兩個例子,來說明一下。

1) realloc() 第一種行為引發的Bug
  1. void *ptr = realloc(ptr, new_size);
  2. if (!ptr) {
  3. // 錯誤處理
  4. }
這裡就引出了一個記憶體洩露的問題,當realloc() 分配失敗的時候,會返回NULL。但是引數中的 ptr 的記憶體是沒有被釋放的。如果直接將realloc()的返回值賦給ptr。那麼當申請記憶體失敗時,就會造成ptr原來指向的記憶體丟失,造成記憶體遊離和洩露。


正確的處理應該是這樣:
  1. void *new_ptr = realloc(ptr, new_size);
  2. if (!new_ptr) {
  3. // 錯誤處理。
  4. }
  5. ptr = new_ptr

2) 第三種行為引發的Bug
實際上,malloc(0)是合法的語句,會返還一個合法的指標,且該指標可以通過free去釋放。這就造成了很多人對realloc()的錯誤理解,認為當size為0時,實際上realloc()也會返回一個合法的指標,後面依然需要使用free去釋放該記憶體。
  1. void *new_ptr = realloc(old_ptr, new_size);
  2. //其它程式碼
  3. free(new_ptr);

由於錯誤的認識,不去檢驗new_size是否為0,還是按照new_size不為0的邏輯處理,最後並free(new_ptr)。這裡就引入了double free的問題,造成程式崩潰。

realloc()函式的返回值是void *型的。有下面三種情況:

1、返回void * 指標,呼叫成功。Void *型的指標指向新分配的記憶體空間。在需要的情況下可以再對這個指標進行強制型別轉換,轉換成你需要的型別的指標。如果傳入的第一個指標引數為NULL,則該函式等同與malloc函式。

2、返回NULL,當需要擴充套件的大小(第二個引數)為0並且第一個引數不為NULL,此時原記憶體被“freed”掉了。

我們知道,realloc是從堆上分配記憶體的,當擴大一塊記憶體空間時, realloc()試圖直接從堆上現存的資料後面的那些位元組中獲得附加的位元組,如果能夠滿足,自然天下太平;可如果資料後面的位元組不夠的話,那麼就使用堆上第一個有足夠大小的自由塊,現存的資料然後就被拷貝至新的位置,而老塊則放回到堆上。返回值指向新分配的記憶體地址。

由於在這其中可能會發生資料的移動,因此我們應該盡力避免下面的用法。

…………………………

#include <malloc.h>

char     *p,*q;

p = (char * ) malloc (10);

q=p;

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

…………………………

在這種情況下,如果發生了資料的移動,p指向了新分配的記憶體地址,但是指標q還依然指向原先的記憶體地址,而原先的那部分記憶體已經在realloc函式中free掉了,因此指標q成了“野指標”,指向了一塊未知的記憶體區域,這是很危險的。類似的,我們也應該儘量避免下面這種情況的使用。

…………………………

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

………………………

與第一種情況類似,如果發生了資料的移動,q指向了新分配的記憶體地址,而指標p還依然指向原先的記憶體地址,此時p成了野指標。如果不得以非得這樣使用的話(估計這種情況是不存在的),我們也應該緊跟此後將指標p置為NULL。

…………………………

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

p = NULL;