C 語言看似簡單,實則暗藏諸多細節陷阱。本文整理十大進階實戰要點,涵蓋符號常數、getchar 回傳型別、編譯器貪婪解析、字元與字串差異、函式指標宣告、運算子優先級、流程控制陷阱、else 配對規則、陣列與指標等價性、邊界條件處理,助你寫出更健壯、可維護的程式碼。
直接在程式碼中硬編碼數字(Magic Number)會嚴重降低可讀性與維護性。應使用 #define 或 const 定義有意義的名稱:
#define MAX_BUFFER 1024 // 符號常數,慣例全大寫
const int MAX_BUFFER = 1024; // 型別安全,編譯器會檢查
| 特性 | #define | const |
|---|---|---|
| 型別檢查 | 無(純文字替換) | 有 |
範例:#define A 3+5 vs const int A = 3+5 | 3 * A → 3 * 3 + 5 = 14 | 3 * A → 3 * 8 = 24 |
switch 可用性 | 可作為 case 標籤 | 不可(非編譯期常數) |
建議:優先使用 const,僅在需前置處理器運算(如 #if)時用 #define。
getchar() 回傳 int 而非 charint c = getchar(); // 必須用 int 接收,以便檢測 EOF(通常為 -1)
若用 char 接收,EOF 與有效字元 0xFF 會混淆,導致迴圈無法正常結束。
// 意圖:y = x / *p; (x 除以 p 指向的值)
y = x/*p; // 編譯器將 /* 視為註解開始,實際編譯為 y = x;
// 正確寫法:加空格或括號
y = x / *p;
y = x / (*p);
同理,x+++++y 會被解析為 x++ + ++y 而非 x++ ++ +y。
'A':單一字元,型別 int(C 語言中字元常數為 int),值為 ASCII 碼 65。"A":字串常數,型別 char*,指向包含 'A' 與 '\0' 兩個位元組的記憶體。float *g(); // 函式 g 回傳 float*
float (*g)(); // 指標 g 指向「回傳 float 的函式」
記憶口訣:* 與識別符結合優先級低於 (),故 (*g) 先結合形成「指標」。
不要依賴記憶中的運算子優先級,明確加括號可避免細微錯誤並提升可讀性:
// 不好
if (a & b == c) ...
// 好
if ((a & b) == c) ...
if (condition); // 空語句!後面的區塊無條件執行
{
doSomething();
}
for (i = 0; i < n; i++); // 迴圈體為空,僅跑計數器
switch (val) {
case 1: doA(); break;
case 2: doB(); // 故意貫穿?請加註釋 /* fall through */
default: doDefault();
}
else 永遠配對最近的 ifif (a)
if (b)
foo();
else
bar(); // 屬於內層 if,非外層
// 明確用大括號表達意圖
if (a) {
if (b) foo();
} else {
bar(); // 現在屬於外層 if
}
C 語言不像 Python 以縮排決定結構,務必養成「有 if 必有大括號」習慣。
void foo(int arr[]) // 等價於 void foo(int *arr)
void foo(int arr[10]) // 同樣退化為指標,10 被忽略
// main 參數的兩種等價寫法
int main(int argc, char *argv[])
int main(int argc, char **argv)
陣列名在表達式中(除 sizeof、&、字串字面值初始化外)自動轉換為指向首元素的指標。
經典範例:n 個柱子有 n-1 段欄桿。迴圈邊界常見錯誤:
// 錯誤:存取 a[n] 越界
for (i = 0; i <= n; i++) a[i] = 0;
// 正確
for (i = 0; i < n; i++) a[i] = 0;
原則:左閉右開區間 [0, n) 為標準迴圈模式。
這十大要點涵蓋 C 語言日常開發中最易踩坑、最具杠桿效應的細節。內化為肌肉記憶,能顯著降低除錯時間、提升程式碼品質。建議收藏備查,並在 Code Review 時重點檢視。