C 語言看似簡單,實則暗藏眾多「細節定成敗」的陷阱。本文基於《程序猿之---C語言細節2》實戰代碼,系統拆解分號誤用、運算符優先級、Switch 缺省穿透、函數指標調用、隱式聲明等高發問題,並給出防禦性編程建議。
struct test { int a; int b; int c; } // 缺少分號
func() { printf("test\n"); } // 被視為 struct test 的返回類型!
若結構體定義後緊跟函數定義且無分號,編譯器會將該函數視為返回該結構體類型的函數。若前置聲明為 int func();,則類型衝突導致編譯錯誤。鐵律:結構體/聯合/枚舉定義結尾必須加分號。
int *p;
// *p++ 等價於 *(p++):先取值,後指針遞增
// (*p)++:取值後,所指對象遞增
後綴 ++ 優先級高於前綴 *,且結合方向為左至右。切記:先看運算符優先級,再看結合性。
switch(i) {
case 3: printf("3\n");
case 1: printf("1\n"); break; // i=3 時會輸出 3 和 1
case 2: printf("2\n");
default: printf("default\n");
}
缺少 break 會導致執行流「穿透」到下一個 case。最佳實踐:若有意穿透,必須加註釋 /* fall through */;否則每個 case 必須以 break/return/throw 結束。
while(i >= 0); // 這裡的分號導致循環體為空!
{ printf("i = %d\n", i); i--; } // 這塊代碼只執行一次
編譯器不報錯,但邏輯完全改變。防範:嚴格遵循「一行一語句」、使用靜態分析工具、代碼審查關注孤立分號。
func(); // 函數調用
func; // 計算函數地址,結果被丟棄,無副作用
C 語言中函數名在表達式中自動轉換為函數指標。func; 是合法但無意義的語句,易混淆。建議:啟用編譯器警告 -Wunused-value。
if (i < 0) return ; // 缺少分號
i = 5; // 若 i>=0,此行被跳過!因為被解析為 return i = 5;
若 return 後無分號,編譯器會繼續讀取下一個 token 作為返回表達式。鐵律:所有語句必須以分號結束,哪怕是空語句。
-Wall -Wextra -Wpedantic(GCC/Clang)或 /W4(MSVC)。clang-tidy、cppcheck 等靜態分析工具納入 CI 流程。assert() 與單元測試覆蓋邊界條件。C 語言的靈活性源於其對程序員的信任,但也要求極高的自律。將這些「細節陷阱」內化為肌肉記憶,配合工具鏈防線,方能寫出健壯、可維護的系統級代碼。