動態配置與釋放記憶體

語法︰

C 語言中容許程式設計者在需要使用記憶體時才自作業系統配置所需的記憶體, 而不需在程式設計時就預估所需要的記憶體。

例如︰

注意

動態配置記憶體的優點︰

  1. 撰寫程式時程式設計者不需要限制程式所能處理的最多資料項目。

    例如:

    如果在程式內限定這些陣列的大小是 50 的話, 這個程式就只能處理最多 50 個學生的資料, 如果學生人數多於 50 的話, 必須要更改程式重新編譯才可以正確工作, 如果是用動態配置的話就沒有這個問題︰ 另外像文書編輯程式、 或是影像編輯應用程式通常不曉得要編輯的文件或是影像有多大, 因此通常以動態配置來取得存放這些資料的記憶體。

  2. 製作資料結構

    例如︰串列 (List),樹 (Tree),不規則的多維陣列 (Triangular, Sparse...)...。

  3. 動態配置的記憶體其生命週期完全由程式設計者來控制:

    其生成在 malloc() 函式, 終結在 free() 函式, 可以在任何看得到此記憶體位址的函式內存取此區域記憶體的變數內容, 不像區域性變數 (local variable) 只有在開始執行一函式時才生成, 函式一完畢立刻就終結, 不能再被使用。

C 程式執行時所有的資料變數置於三種區域:

  1. 資料區 (Data segment)︰ 全域變數, static 變數,常數。

  2. 堆疊區 (Stack)︰ 區域變數 (Auto variable), 函式參數,暫時變數。

  3. Heap 區︰ 動態配置的記憶體。

注意

malloc() 函式及 free() 函式缺一不可, 必須成對執行︰

  1. 若藉由指標變數使用動態記憶體時, 該指標變數之內容不正確時, 小則產生邏輯錯誤, 大則產生 illegal access 的執行錯誤而使程式停止。

    下面狀況會產生不正確的指標︰

    1. 配置記憶體失敗
        int *ptr; ptr = (int*) malloc(100);

    2. 忘記配置記憶體
        int *ptr; *ptr = 1;

    3. 記憶體已經釋放掉還繼續使用 (稱為 dangling reference)
        int *ptr; : : ptr = (int *)malloc(100); : : free(ptr); *(ptr+10) = 100;
      注意
        dangling reference 除了上述這種狀況之外還有一種, 就是函式傳回一區域變數 (local variable) 之指標:
          double *square(double x) { double y; y = x * x; return &y; } void main(void) { double x=2.5, *y; y = square(x); printf("The square of %lf is %lf\n", x, *y); }

    4. 配置的記憶體不夠使用
        double *ptr; ptr = (double *) malloc(100); . . . ptr[99] = 20.0;

  2. 如果配置了記憶體在用完了以後沒有使用 free() 函式還給系統的話, 這些記憶體資源只有在程式結束後才能夠被其他程式使用, 例如你可以用下面的程式碼測試看看作業系統能夠給你多少記憶體資源? 如果你改用 的話, 你很可能會得到一個無窮迴圈, sum 變數內的數值會毫無限制地增加下去。

  3. memory leakage

    另一個在前面範例裡出現的問題是: 當你配置完記憶體後你會得到一個指標, 這個指標就好像鎖匙一樣, 是你存取此段記憶體區塊內資料唯一的媒介, 如果你弄丟了的話你就沒辦法再存取這段記憶體, 也沒有辦法釋放此段記憶體,

    例如:

    在這樣子的程式碼中第一個敘述配置的記憶體記錄在 ptr 中, 而在第二個敘述中此 ptr 變數內的資料被覆蓋掉, 以後程式內就永遠不記得剛才此指標的內容了, 同時也就沒有辦法存取第一次配置的那些記憶體了, 此種狀況稱為 memory leakage, 一般來說作業系統能夠給你的應用程式的記憶體是有限的, 如果你的程式持續不斷地弄丟掉一些記憶體的話, 你能夠配置的記憶體就會越來越小, 導致你的應用程式最後發現沒有記憶體可用而需要結束。

  4. 不可重覆 free 某一指標 此舉常會造成動態記憶體管理模組的錯誤, 此與前述的 dangling reference 問題是同樣性質的錯誤, 一個指標 free 掉以後程式內就不應該再去使用它 (包括釋放它), 通常我們會有一些保護措施, 如下: 藉由 iPtr 是否為 0 可以知道 iPtr 是否目前指向 一個程式可以使用的記憶體區段, 在配置新的記憶體之前必須檢查一下, 以確保不會造成 memory leakage, 在釋放掉此記憶體區段以後必須立刻麼指標設為 0, 以避免不慎誤用 (dangling reference)。

Dangling Reference II

前面我們曾經麼指標比喻為存取記憶體內容的一把鎖匙, 這把鎖匙掉了會造成 Memory leakage, 那麼複製是不是可以防止遺失的困擾呢? 任意地複製一樣會造成麻煩: 這開啟了 dangling reference 的大門, 我們在釋放掉一段記憶體時很容易地就記得銷毀掉那支鎖匙, 但是複製下來到處存放的指標很難去記得毀掉了, 因此對於複製必須同時存在的指標要特別小心地處理。

注意

與 C++ 的 new/delete 不可混用

使用 new 運算子配置的記憶體必須用 delete 運算子釋放, 使用 malloc() (或是 calloc()) 函式配置的記憶體必須用 free() 函式釋放。

某些編譯器支援在 stack frame 上動態配置記憶體

在 Visual C 中可以用特殊的 alloc 函式自堆疊上配置記憶體, 此種記憶體和區域變數 (local variables) 一樣, 可以不需要以 free() 函式釋放, 一旦結束此函式即自動釋放此段記憶體。

程式設計課程 首頁

製作日期: 98/11/30 by 丁培毅 (Pei-yih Ting)
E-mail: pyting@mail.ntou.edu.tw