【在主畫面加入捷徑】
       
【選擇語系】
繁中 简中

[C 語言] 程式設計教學:指標 (Pointer) 和記憶體管理 (Memory Management)

Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email
【贊助商連結】

    指標 (pointer) 是 C (或 C++) 中用來管理記憶體的語法特性,指標內儲存的值 (value) 是記憶體的虛擬位置 (virtual address)。一般學 C (或 C++) 時產生的陰影一部分就是來自於指標,但指標其實並不是什麼洪水猛獸,使用一陣子就會自然而然地習慣其使用方式。

    前言

    由於指標有許多的使用情境,我們將其拆開在多篇文章中:

    有需要的讀者可以前往觀看。

    (影音教學) 宣告指標變數的方式

    以下影片介紹在 C 語言中常見的宣告指標變數的方式:

    這個影像的內容不影響文章,有興趣的讀者可自行觀賞。

    (影音教學) 指標和記憶體管理

    以下影片以數個常見的實例來介紹在 C 標準函式庫中和記憶體管理相關的函式:

    這個影片是選擇性的,讀者可自行觀看。

    C 語言的記憶體管理模式

    了解 C 語言中記憶體管理的模式,也會對使用指標有幫助。在 C 語言中,記憶體管理有三種層次:

    • 靜態記憶體管理 (static memory management)
    • 自動記憶體管理 (stack memory management)
    • 手動記憶體管理 (heap memory management)

    靜態配置記憶體適用於程式碼本身、靜態變數 (static variable)、全域變數 (global variable) 等。雖然靜態記憶體管理不需人為介入,但只能用於固定值的變數,而且有大小的限制,故無法適用於所有的情境。此外,在程式中濫用全域變數也是不良的習慣。

    自動配置記憶體的值會隨著函式的生命週期結束而消失,在主函式以外的函式中使用局部變數 (local variable) 時皆需考慮這項特性。雖然自動配置記憶體不需人為介入,但局部變數無法跨函式傳遞,而且局部變數同樣有大小的限制,故不適用於所有的情境。

    手動配置記憶體的大小約略等於系統的記憶體大小,也就是說,可以充分利用系統資源。此外,使用手動配置的局部變數可跨函式傳遞,在實際應用上會方便地多。但手動配置的記憶體之後需要手動釋放掉,這就是我們要學習的地方。

    宣告和使用指標

    以下的例子使用到 stack memory,所以不需手動釋放記體體:

    #include <assert.h>
    #include <stdlib.h>
    
    int main(void)
    {
        // An automatic variable using stack memory.
        int i = 3;
        
        // Declare a pointer to int.
        int *i_p = NULL;
        
        // Point to the address of i.
        i_p = &i;
        assert(*i_p == 3);
        
        // Change the value of `i` through `i_p`
        *i_p = 5;
        assert(i = 5);
        
        return 0;
    }

    首先,我們宣告並指派變數 i,程式已經配置好一塊 stack memory。

    接著我們宣告一個指向整數的指標 i_p,宣告的方式是在變數旁加上星號 *;以本例來說,*i_p 是一個指向整數的指標。如果宣告指標時沒有要將該指標指向某個位址,最好先將其值設為 NULL

    在本例中,我們將 i_p 的位址指向 i 所在的位址,對變數 i 前加上 & 可以取得其位址。

    我們透過指標 i_p 間接修改 i 的值,並用斷言確認 i 有改到。

    細心的讀者可以發現,本例沒有引入 stdlib.h 但程式可正常運作。可知指標和記憶體配置函式不一定要掛勾。如果我們用 Valgrind 檢查此程式,可發現此程式沒有從 heap 分配記憶體,也沒有造成記憶體洩露。

    像以下的例子有配置 heap memory,就需手動釋放:

    #include <stdlib.h>
    
    int main()
    {
        // Allocate a chunk of heap memory.
        int *i_p = malloc(sizeof(int));
        if (!i_p)
            return 1;
        
        // Free i_p.
        free(i_p);
        
        return 0;
    }

    配置記憶體的函式為 malloc,配置時需傳入記體體的大小,通常是搭配 sizeof 來取得某個型別的大小。平常練習時 malloc 大多都會成功,但其實 malloc 有可能會失敗,最好還是加入錯誤檢查的程式碼。在本例中,當 malloc 失敗時就中止程式,並回傳程式失敗的狀態碼。釋放記憶體的函式為 free,將指標傳入即可釋放記憶體。處理記憶體的函式位於 stdlib.h 函式庫中。

    mallocfree 應當成對出現,每當有用 malloc (或其他同質函式) 配置 heap memory,就要於後續的程式中搭配 free 來釋放記憶體。

    如果我們用 Valgrind 檢查此程式,可發現此程式有從 heap 配置記憶體,但已釋放了,沒有造成記憶體洩露。

    小整理

    C 語言中指標相關的運算子有兩種:

    • &
    • *

    & 的意義皆為取址 (address of value)。

    在宣告變數時加上 * 代表宣告該變數為指標,在其他地方使用 * 則是對該指標變數取值 (value of address)。

    void 指標

    void 指標 (void *) 可指向任意指標型別,像是 free 函式的參數即為 void 指標。利用 void 指標可在 C 語言中模擬泛型程式的特性。

    延伸閱讀

    如果想要深入學習指標,可以閱讀 Understanding and Using C Pointers, O’Reilly (2013)

    【贊助商連結】
    標籤: C 語言, POINTER
    【贊助商連結】
    【分類瀏覽】