C 編譯器前端的詞法分析器負責將原始碼切分為最小語法單元(Token)。理解其運作規則——特別是「最大吞噬原則」——是避開經典陷阱的關鍵。
= 與 == 的致命混淆int i = 10, j = 20;
if (i = j) { // 本意是 i == j
printf("i equal j\n");
}
i = j 是賦值運算,值為 20(非零)。if 判斷條件為真,永遠執行 printf。i 被意外修改為 20,後續邏輯全亂。int i = 10, j = 0;
if (i = j) { // 賦值後 i=0,條件為假
printf("i equal j\n"); // 不會執行
}
-Wall -Wextra (GCC/Clang) 或 /W4 (MSVC),會提示 warning: suggest parentheses around assignment used as truth value。if (20 == i),誤寫成 = 會直接編譯報錯(無法賦值給常數)。a---b 是什麼?int a = 10, b = 2;
printf("a---b = %d\n", a---b); // 輸出 8
依「最大吞噬原則」,字元流 a - - - b 被切分為:
a -- - b
│ │ │ │
│ │ └── 減法運算子
│ └───── 後置遞減運算子 (a--)
└──────── 標識符 a
等價於 (a--) - b:
a 的值 10 參與減法。a 遞減為 9。10 - 2 = 8。a - --b → a - (--b) → 10 - 1 = 9(先遞減 b 再相減)。a - - b → a - (-b) → 10 - (-2) = 12(減去負數)。加空格! a-- - b 或 a - --b 讓意圖一目瞭然,別讓維護者(包括未來的自己)猜謎。
int a = 11, b = 011;
printf("a = %d, b = %d\n", a, b); // 輸出:a = 11, b = 9
0 開頭的整型常量視為八進制。011 (八進制) = 1×8¹ + 1×8⁰ = 9 (十進制)。0o11 明確八進制前綴;C23 也將支援。int month = 09; // 編譯錯誤!9 不是合法八進制數字
// 或更隱蔽:
int perms = 0777; // 正確八進制
int perms2 = 0787; // 錯誤:8 非法
0755),絕不寫前導零。0o 前綴或十進制。'a' vs 字符串 "a"| 特性 | 字符 'a' | 字符串 "a" |
|---|---|---|
| 型別 | int (C) / char (C++) | char * (指向陣列) |
| 大小 | sizeof('a') == sizeof(int) (通常 4) | sizeof("a") == 2 (含結束符 \0) |
| 存儲區 | 立即數,編譯期確定 | 常量區 (唯讀記憶體) |
| 可修改 | N/A (非左值) | 不可修改 (UB) |
| 用法示例 | switch(ch) { case 'a': ... } | printf("hello"); |
char *p = "hello";
p[0] = 'H'; // 未定義行為!嘗試修改唯讀記憶體
// 正確做法:
char arr[] = "hello"; // 陣列在棧上,可修改
arr[0] = 'H'; // OK
printf("Hello world\n");
// 等價於
char hello[] = {'H','e','l','l','o',' ','w','o','r','l','d','\n', 0};
printf(hello);
0o 或十進制。'a' 是整數,"a" 是指標;字符串常量不可修改。掌握這些詞法細節,能讓你在 Code Review 時一眼識破隱藏 Bug,也能寫出更健壯、可維護的 C 代碼。