空指針:值為 0 (NULL) 的指針,**不指向任何有效內存對象**。標準宏 NULL 定義為 ((void*)0) (C) 或 0/nullptr (C++)。
| 概念 | 本質 | 示例 | 可解引用? |
|---|---|---|---|
| 空指針 | 指針值 = 0 | int *p = NULL; int *p = 0; | ❌ 未定義行為 (段錯誤) |
| void* 指針 | 無具體類型的通用指針 | void *p = malloc(10); | ❌ 必須轉型後才能解引用 |
| 字符串結束符 '\0' | 字符值 = 0 (ASCII NUL) | char *s = "hello"; // s[5] == '\0' | ✅ 是合法字符,非指針 |
int *p; (局部變量值不定,**非**自動為 NULL,屬「野指針」)int *p = NULL;int *p = malloc(SIZE_MAX); // 返回 NULLfree(p); p = NULL; // 防止懸空指針二次釋放fopen("none.txt", "r") // 返回 NULL
// 1. 分配後檢查
int *arr = malloc(n * sizeof(int));
if (!arr) { perror("malloc failed"); exit(EXIT_FAILURE); }
// 2. 文件/庫函數返回檢查
FILE *fp = fopen("data.txt", "r");
if (!fp) { fprintf(stderr, "Open failed: %s\n", strerror(errno)); return -1; }
// 3. 函數參數合法性斷言 (調試版)
void process(int *ptr) {
assert(ptr != NULL); // debug 版觸發中斷,release 版無開銷 (NDEBUG)
// 或運行時檢查返回錯誤碼
if (!ptr) return ERR_NULL_PTR;
}
// 4. 釋放後置空 (防止 double free / use-after-free)
free(ptr);
ptr = NULL;
| 誤區 | 正確認知 |
|---|---|
void* 是空指針 | void* 是「無類型指針」,可指向任意類型數據,**非**空指針。void *p = malloc(10); 指向有效內存。 |
NULL == '\0' | NULL 是指針上下文的 0,'\0' 是字符常量 (整型值 0)。類型語義完全不同,不可混用。 |
| 全局/靜態指針自動為 NULL | **靜態存儲期**變量 (全局/靜態) 未顯式初始化**保證為 0/NULL**。局部變量 (自動存儲期) 未初始化值**不定** (野指針)。 |
if (p) 等價 if (p != NULL) | **完全等價**。C 語言標準規定指針在標量上下文中,非零為真,零為假。推薦寫 if (!ptr) 簡潔明確。 |
| 空指針解引用只會崩潰 | 屬**未定義行為 (UB)**:可能段錯誤、可能沉默破壞數據、可能被優化器刪除周邊代碼、**絕不可依賴具體表現**。 |
// 註解指針非空契約 (Clang/GCC 支持)
void foo(int * _Nonnull ptr); // 調用者必須保證非 NULL
void bar(int * _Nullable ptr); // 允許傳 NULL,函數內需檢查
// 靜態分析工具
// clang-tidy -checks='clang-analyzer-core.*' // 核心檢查器
// cppcheck --enable=all --std=c11 --force . // 跨平台靜態分析
// scan-build make // Clang Static Analyzer
空指針 = 值為 0 的指針,不指向有效對象。
牢記三不混:**非 void\***、**非 '\0'**、**非未初始化局部指針**。
工程鐵律:**分配檢查、參數斷言、釋放置空、靜態分析**。
掌握這些,段錯誤減少 90%,代碼健壯性質變提升。