1. 程式人生 > 其它 >(C語言記憶體二十)C語言記憶體洩露(記憶體丟失)

(C語言記憶體二十)C語言記憶體洩露(記憶體丟失)

記憶體洩漏

使用 malloc()、calloc()、realloc() 動態分配的記憶體,如果沒有指標指向它,就無法進行任何操作,這段記憶體會一直被程式佔用,直到程式執行結束由作業系統回收。

請看下面的程式碼:

#include <stdio.h>
#include <stdlib.h>
int main(){
    char *p = (char*)malloc(100 * sizeof(char));
    p = (char*)malloc(50 * sizeof(char));
    free(p);
    p = NULL;
    return 0;
}

該程式中,第一次分配 100 位元組的記憶體,並將 p 指向它;第二次分配 50 位元組的記憶體,依然使用 p 指向它。

這就導致了一個問題,第一次分配的 100 位元組的記憶體沒有指標指向它了,而且我們也不知道這塊記憶體的地址,所以就再也無法找回了,也沒法釋放了,這塊記憶體就成了垃圾記憶體,雖然毫無用處,但依然佔用資源,唯一的辦法就是等程式執行結束後由作業系統回收。

這就是記憶體洩露(Memory Leak),可以理解為程式和記憶體失去了聯絡,再也無法對它進行任何操作。

記憶體洩漏形象的比喻是“作業系統可提供給所有程式使用的記憶體空間正在被某個程式榨乾”,最終結果是程式執行時間越長,佔用記憶體空間越來越多,最終用盡全部記憶體空間,整個系統崩潰。

再來看一種記憶體洩露的情況:

int *pOld = (int*) malloc( sizeof(int) );
int *pNew = (int*) malloc( sizeof(int) );

這兩段程式碼分別建立了一塊記憶體,並且將記憶體的地址傳給了指標 pOld 和 pNew。此時指標 pOld 和 pNew 分別指向兩塊記憶體。

如果接下來進行這樣的操作:

pOld=pNew;

pOld 指標就指向了 pNew 指向的記憶體地址,這時候再進行釋放記憶體操作:

free(pOld);

此時釋放的 pOld 所指向的記憶體空間就是原來 pNew 指向的,於是這塊空間被釋放掉了。但是 pOld 原來指向的那塊記憶體空間還沒有被釋放,不過因為沒有指標指向這塊記憶體,所以這塊記憶體就造成了丟失。

另外,你不應該進行類似這面這樣的操作:

malloc( 100 * sizeof(int) );

這樣的操作沒有意義,因為沒有指標指向分配的記憶體,無法使用,而且無法通過 free() 釋放掉,造成了記憶體洩露。

最後的總結

free() 函式的用處在於實時地回收記憶體,如果程式很簡單,程式結束之前也不會使用過多的記憶體,不會降低系統的效能,那麼也可以不用寫 free() 函式。當程式結束後,作業系統會釋放記憶體。

但是如果在開發大型程式時不寫 free() 函式,後果是很嚴重的。這是因為很可能在程式中要重複一萬次分配10MB的記憶體,如果每次進行分配記憶體後都使用 free() 函式去釋放用完的記憶體空間, 那麼這個程式只需要使用10MB記憶體就可以執行。但是如果不使用 free() 函式,那麼程式就要使用100GB 的記憶體!這其中包括絕大部分的虛擬記憶體,而由於虛擬記憶體的操作需要讀寫磁碟,因此,這樣會極大地影響到系統的效能,系統因此可能崩潰。

因此,在程式中使用 malloc() 分配記憶體時都對應地寫出一個 free() 函式是一個良好的程式設計習慣。這不但體現在處理大型程式時的必要性,並能在一定程度上體現程式優美的風格和健壯性。