野指標、懸空指標、空指標和記憶體洩漏是記憶體管理中四個重要概念。
野指標:指標變數未被初始化,或者被賦予了一個隨意的、無效的地址值。它指向的記憶體區域可能是隨機的,使用野指標會導致程式出現不可預測的行為,如程式崩潰或資料損壞
懸空指標:指標原來指向的記憶體已經被釋放(例如透過free函式),但指標本身沒有被置為NULL,仍然儲存著之前記憶體的地址。再嘗試訪問這個指標所指向的已釋放記憶體,就會產生錯誤。
空指標:一個特殊的指標值,它不指向任何有效的記憶體地址。C語言中通常用NULL表示,可用於初始化指標變數,或者在指標不指向有效地址時作為一個明確的標識。對空指標進行解引用操作是不允許的,會導致程式出錯。
記憶體洩漏:指程式在動態分配記憶體(例如使用malloc、calloc等函式)後,沒有釋放這些記憶體,導致這些記憶體塊在程式執行過程中一直被佔用,無法被作業系統重新利用。隨著程式的執行,記憶體洩漏會逐漸消耗系統記憶體,導致程式效能下降甚至系統崩潰。
總結,野指標和懸空指標指向無效記憶體(通常可以統一稱為野指標),空指標則明確不指向任何記憶體,而記憶體洩漏則是未釋放的有效記憶體,三者均可能導致程式錯誤和效能問題。
野指標程式碼示例
#include <stdio.h>
int main() {
int *wild_ptr;
// 沒有初始化wild_ptr,它是野指標
// 下面這行程式碼可能會導致錯誤,因為wild_ptr指向的位置不確定
*wild_ptr = 5;
return 0;
}
在這個例子中,指標wild_ptr沒有被初始化就進行解引用操作,它指向的位置是不確定的,可能會導致程式崩潰或者出現不可預期的行為。
空指標程式碼示例
#include <stdio.h>
int main() {
int *null_ptr = NULL;
// 下面這行程式碼會導致程式出錯,因為不能對空指標解引用
*null_ptr = 10;
return 0;
}
懸空指標程式碼示例
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int *)malloc(sizeof(int));
*ptr = 10;
free(ptr);
// ptr現在是懸空指標,下面的操作會導致未定義行為
*ptr = 20;
return 0;
}
在這裡,透過free函式釋放了指標ptr所指向的記憶體空間,之後ptr就變成了懸空指標。再對ptr進行解引用操作是不合法的,會導致未定義行為。
記憶體洩漏程式碼示例
#include <stdio.h>
#include <stdlib.h>
void func() {
int *leak_ptr = (int *)malloc(sizeof(int));
*leak_ptr = 5;
// 沒有釋放leak_ptr所指向的記憶體,產生記憶體洩漏
}
int main() {
memory_leak_function();
// 函式呼叫結束後,之前分配的記憶體沒有被釋放
return 0;
}
在func函式中,動態分配了一塊記憶體給指標leak_ptr,但是在函式結束時沒有釋放這塊記憶體。這樣,每次呼叫該函式,就會有一塊記憶體被佔用而無法回收,造成記憶體洩漏。如果頻繁呼叫,會導致程式記憶體佔用越來越大,最終可能耗盡系統資源。
面試建議
- 對於野指標、懸空指標、空指標和記憶體洩露,是我們開發過程中經常會遇到的問題,一定要理解其原理以及如何規避。這是計算機非常重要的基礎知識。
- 在移動端開發過程中,記憶體洩漏是一個常見的問題,可能導致應用效能下降或崩潰。
以下是幾種常見的記憶體洩漏場景
使用閉包(closure)時,如果閉包強引用了外部物件,而外部物件又強引用了閉包,就會形成迴圈引用,導致記憶體無法釋放。
在使用資源(如影像、影片或資料庫連線)時,如果沒有在不需要的時候釋放它們,可能會導致記憶體洩漏。
如何規避記憶體洩漏:
使用弱引用:避免迴圈引用。
資源管理:確保在不需要時釋放大型資源。
使用記憶體分析工具:如 Android Profiler 或 Xcode Instruments,幫助檢測記憶體洩漏。