【C 語言入門】185.釋放動態配置的記憶體

練習

讓使用者可以輸入任意個整數後,將所有輸入的整數一起印出(輸入 0 表示結束)。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int *numbers;                //儲存輸入的數字
    int length = 0;              //目前已經輸入的數字個數
    while(1){
        int input;
        scanf("%d", &input);     //從鍵盤輸入數字
        if(input == 0) break;    //如果輸入的數字為0則結束
        int* larger = malloc(sizeof(int) * (length+1));   //產生一個大一格的新陣列
        for(int i =0; i < length ;i++) larger[i] = numbers[i];//複製舊陣列到新陣列
        numbers = larger;        //將 numbers 設為剛產生的新陣列
        numbers[length] = input; //將輸入數字放在最後面
        length++;                //將已輸入的數字個數加1
    }
    printf("Numbers: ");
    for(int i = 0; i < length; i++){ //數每個輸入的數字
        printf("%d", numbers[i]);
    }
    printf("\n");
    return 0;
}

這個程式執行起來沒有問題,不過我們會遇到一個小問題。

當我們一邊配置更大的陣列的時候,原本比較小的陣列的記憶體空間,不會自動地被歸還。

這是因為當初我們配置它的時候,我們使用的 malloc 函式是不會自動歸還的。

這個問題我們稱之為記憶體洩漏 (Memory Leak) 。

我們一邊執行,也會一邊產生沒有被使用,但也沒有被歸還的記憶體。

用了記憶體但沒有歸還,最後就會造成我們的記憶體被用完或是被浪費。

C 語言提供了一個叫 free 的函式。

<stdlib.h> 提供了 free 函式,我們可以使用 free 函式釋放動態配置的記憶體。

void free(void* ptr);

free 這個函式只有一個參數 ptr ,ptr 是一個指標。

ptr 就是要釋放的動態記憶體位址。

該記憶體空間是由 malloc 或其他動態配置記憶體函式配置的。

在我們一開始介紹的程式中,我們有用新的陣列來取代舊的陣列。

numbers = larger; 

這時候就陣列就沒有用了,我們可以在用新的陣列取代就陣列之後,再做釋放的動作嗎?

其實是不行的。

numbers 儲存的記憶體位置,被改為新的記憶體位置之後,原本的記憶體位址就消失了,因此我們沒辦法釋放原本的位置。

換句話說,我們需要先釋放原本的記憶體,再讓 numbers 指向新的記憶體的位置。

因此當 malloc 配置好新的記憶體空間的時候,我們先把舊的資料搬到新的記憶體空間,此時就地陣列就可以被釋放了。

這是我們再讓 numbers 指向新的陣列就 ok 了。

一開始我們 numbers 的指標,我們並沒有初始化它,沒有讓它指向任何陣列。

我們可以使用空指標。

空指標是一個指標的特殊值,代表這個指標沒有指向任何記憶體空間。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int *numbers = 0;            //0 表示空指標 (NULL)
    int length = 0;              //目前已經輸入的數字個數
    while(1){
        int input;
        scanf("%d", &input);     //從鍵盤輸入數字
        if(input == 0) break;    //如果輸入的數字為0則結束
        int* larger = malloc(sizeof(int) * (length+1));   //產生一個大一格的新陣列
        for(int i =0; i < length ;i++) larger[i] = numbers[i];//複製舊陣列到新陣列
        free(numbers);
        numbers = larger;        //將 numbers 設為剛產生的新陣列
        numbers[length] = input; //將輸入數字放在最後面
        length++;                //將已輸入的數字個數加1
    }
    printf("Numbers: ");
    for(int i = 0; i < length; i++){ //數每個輸入的數字
        printf("%d", numbers[i]);
    }
    printf("\n");
    return 0;
}

如果我們一開始給 numbers 指標 0,那我們在呼叫 free 函式的時候, free 函式會檢查你傳進去的位置是不是 0。

像這個例子中,numbers 如果是 0 的話,它就知道 numbers 其實根本沒有指向任何記憶體空間,也就不會做任何事情。

以前我們提過,數值如果沒有初始化就去存取是有風險的,會發生未定義行為。

其實指標也一樣,指標的變數,如果沒有初始化就去存取,也會是一個未定義行為。

Leave a Comment

Your email address will not be published. Required fields are marked *