釋放記憶

可以通過呼叫 free() 來釋放動態分配的記憶體。

int *p = malloc(10 * sizeof *p); /* allocation of memory */
if (p == NULL) 
{
    perror("malloc failed");
    return -1;
}

free(p); /* release of memory */
/* note that after free(p), even using the *value* of the pointer p
   has undefined behavior, until a new value is stored into it. */

/* reusing/re-purposing the pointer itself */
int i = 42;
p = &i; /* This is valid, has defined behaviour */

p 指向的記憶體在呼叫 free() 之後被回收(通過 libc 實現或底層作業系統),因此通過 p 訪問該釋放的記憶體塊將導致未定義的行為 。引用已釋放的儲存器元素的指標通常稱為懸空指標 ,並存在安全風險。此外,C 標準規定即使訪問懸空指標的值也有未定義的行為。請注意,指標 p 本身可以重新使用,如上所示。

請注意,你只能在 malloc()calloc()realloc()aligned_alloc() 函式中直接返回的指標上呼叫 free(),或者文件告訴你記憶體已經分配的方式(像 strdup () 這樣的函式是值得注意的例子)。釋放一個指標,

  • 通過在變數上使用 & 運算子獲得的,或者
  • 在分配的塊的中間,

禁止。這樣的錯誤通常不會被編譯器診斷出來,但會導致程式在未定義的狀態下執行。

有兩種常見的策略可以防止這種未定義行為的例項。

第一個也是最好的很簡單 - 當不再需要時,p 本身不再存在,例如:

if (something_is_needed())
{

    int *p = malloc(10 * sizeof *p);
    if (p == NULL) 
    {
        perror("malloc failed");
        return -1;
    }

    /* do whatever is needed with p */

    free(p);
}

通過在包含塊(即 })結束之前直接呼叫 free()p 本身不再存在。編譯器將在任何嘗試使用 p 之後給出編譯錯誤。

第二種方法是在釋放指向它的記憶體後使指標本身無效:

free(p);
p = NULL;     // you may also use 0 instead of NULL

這種方法的論據:

  • 在許多平臺上,嘗試取消引用空指標將導致即時崩潰:分段錯誤。在這裡,我們至少得到一個指向被釋放後使用的變數的堆疊跟蹤。

    如果沒有設定指向 NULL 的指標,我們就有懸空指標。該程式很可能仍然會崩潰,但後來,因為指標指向的記憶體將無聲地被破壞。這些錯誤很難追蹤,因為它們可能導致與初始問題完全無關的呼叫堆疊。

    因此,這種方法遵循快速失敗的概念

  • 釋放空指標是安全的。在 C 標準規定的是 free(NULL) 沒有影響:

    free 函式導致 ptr 指向的空間被釋放,即可用於進一步分配。如果 ptr 是空指標,則不執行任何操作。否則,如果引數與 callocmallocrealloc 函式先前返回的指標不匹配,或者如果通過呼叫 freerealloc 釋放了空間,則行為未定義。

  • 有時第一種方法不能使用(例如,記憶體在一個函式中分配,並且在一個完全不同的函式中稍後釋放)