釋放記憶
可以通過呼叫 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 是空指標,則不執行任何操作。否則,如果引數與
calloc
,malloc
或realloc
函式先前返回的指標不匹配,或者如果通過呼叫free
或realloc
釋放了空間,則行為未定義。 -
有時第一種方法不能使用(例如,記憶體在一個函式中分配,並且在一個完全不同的函式中稍後釋放)