C 語言給予程式設計師極大自由,但也埋下無數隱蔽陷阱。本文整理六大類高頻 Bug,涵蓋動態記憶體、字串處理、條件判斷、類型轉換、指標運算與變長參數,每項附帶錯誤代碼、根因分析與最佳實踐,助你繞過雷區寫出健壯代碼。
調用 `free(ptr)` 時程式中斷,調試器報告 `user breakpoint called from code at 0x...`。
// 宏封裝:釋放後置 NULL,防重複釋放
#define SAFE_FREE(p) do { if(p) { free(p); (p)=NULL; } } while(0)
// 使用範例
int *p = malloc(100 * sizeof(int));
// ... 使用 p ...
SAFE_FREE(p); // 安全釋放
// SAFE_FREE(p); // 二次調用無害
鐵律:嚴守分配邊界,`malloc` 多少字節,只能訪問這些字節;釋放前確保指標未偏移。
char *s = "Hello World"; // 指向唯讀資料段
s[0] = 'h'; // 執行時崩潰!嘗試修改唯讀記憶體
char a[] = "Hello World"; // 分配在棧上,可修改
a[0] = 'h'; // 正常
需要修改的字串必須用陣列或 `malloc` 動態分配;指向常量的指標應加 `const`:`const char *s = "..."`,編譯器將阻止誤修改。
C 語言邏輯運算子 `&&`、`||` 具備短路特性:左側條件足以決定結果時,右側不執行。善用此特性可避免空指標解引用、陣列越界、除零錯誤。
// 1. 陣列反向遍歷防越界
while (j >= 0 && a[j] > 0) j--; // j<0 時不評估 a[j]
// 2. 除法前檢查除數
if (d != 0 && n / d == 0) { ... }
// 3. 指標有效性檢查
if (p == NULL || *p == '\0') { /* 空字串處理 */ }
交換條件順序:`if (*p == '\0' || p == NULL)` → 當 `p==NULL` 時先解引用導致崩潰。
int a = -1;
unsigned int b = 1;
if (a > b) printf("a > b"); // 輸出 "a > b"!因 -1 被提升為巨大無符號數
運算中有無符號數時,有符號數隱式轉換為無符號數。編譯器可能採用「值保持」或「無符號保持」規則,行為不可預期。
char *p = ...;
((int *)p)++; // 錯誤!轉型表達式是右值,不可自增
// 方法 1:轉型後運算再轉回
p = (char *)((int *)p + 1);
// 方法 2:直接位元組偏移(推薦,清晰且無嚴格別名違規風險)
p += sizeof(int);
原理:C 標準規定轉型操作產生右值,不可賦值或自增。指標運算以指標類型大小為單位,`char*` 步長 1 位元組最靈活。
__int64 n = 1;
int b = 2;
printf("%d, %d\n", n, b); // 輸出 "1, 0" 而非預期 "1, 2"
`printf` 等變長參數函數無原型約束,編譯器不執行預設類型轉換。`__int64` (8 位元組) 在棧上佔兩個 `int` 槽位,導致後續參數讀取位置錯位。
內化這些規則,能消除 90% 以上的 C 語言低級錯誤,讓你專注於業務邏輯而非調試詭異崩潰。