語法錯誤:原始碼違反程式語言文法規則(詞法/語法分析階段),導致編譯器無法生成合法的抽象語法樹(AST),從而拒絕編譯。
類比自然語言:「我 去 商店 買 蘋果」✅ 符合語法;「蘋果 買 商店 我 去」❌ 語法錯誤(雖然詞彙正確,但結構違規)。
源碼 (.c)
↓ 詞法分析 -> Token 流 (關鍵字、標識符、運算符、字面量...)
↓ 語法分析 -> AST (抽象語法樹) ← 這裡發生語法錯誤
↓ 語義分析 -> 帶註釋 AST (型別檢查、作用域解析...) ← 語義錯誤
↓ 中間代碼生成/優化
↓ 目標代碼生成
↓ 鏈接 -> 可執行檔
| 錯誤類型 | 階段 | 典型例子 | 編譯器行為 |
|---|---|---|---|
| 語法錯誤 | 語法分析 | 缺少分號、括號不匹配、關鍵字拼寫錯、語句結構無效 | 報錯,停止編譯 |
| 語義錯誤 | 語義分析 | 型別不匹配、未聲明標識符、重複定義、訪問私有成員 | 報錯,停止編譯 |
| 連結錯誤 | 連結階段 | 未定義引用 (函數/變量)、重複符號定義、庫缺失 | 報錯,不生成可執行檔 |
| 運行時錯誤 | 程式執行 | 除零、空指標解引用、越界訪問、棧溢出、斷言失敗 | 程式崩潰/異常終止 |
| 邏輯錯誤 | 程式執行 | 演算法錯誤、邊界條件遺漏、業務規則實現錯誤 | 程式正常運行但結果錯誤 |
int main() {
int a = 10 // ❌ 缺少分號
printf("%d", a) // ❌ 缺少分號
return 0;
}
if (a > 0) { // ❌ 缺少右大括號
printf("positive");
whille (i < 10) { ... } // ❌ while 拼寫錯
int retrun = 0; // ❌ return 是關鍵字,不能作變量名
int 2var = 0; // ❌ 標識符不能以數字開頭
a + b = c; // ❌ 左值必須可修改的實體 (lvalue)
break; // ❌ break 不在循環/switch 中
case 1: // ❌ case 不在 switch 中
default:; // ❌ default 不在 switch 中
goto label; // ✅ 語法合法 (但不推薦濫用)
label: ; // ✅ 標籤語句
#include // ✅
include // ❌ 缺少 #
#define MACRO // ✅
#defien MACRO // ❌ 指令拼寫錯
#if CONDITION // ✅
#fi // ❌ 應為 #endif
int ; // ❌ 缺少聲明符
int a, ; // ❌ 尾隨逗號 (C99 允許枚舉/結構體成員尾隨逗號,但變量聲明不允許)
const int = 5; // ❌ 缺少標識符
int func() { // ✅ 隱式 int 返回類型 (C89 允許,C99+ 建議顯式 int func(void))
return 0;
}
/* GCC/Clang 典型輸出 */
test.c:5:15: error: expected ';' before 'printf'
5 | printf("%d", a)
| ^
| ;
/* MSVC 典型輸出 */
test.c(5): error C2143: syntax error: missing ';' before 'printf'
語法錯誤是「編譯器聽不懂你在說什麼」。它是最低級、最容易修復、最不應出現在提交代碼中的錯誤。養成寫一行、編譯一次、修復所有警告的習慣,配合現代 IDE 的即時反饋,可將語法錯誤消滅在萌芽狀態。真正的挑戰在語義錯誤、邏輯錯誤與運行時 Bug——那才是程式設計師的核心價值所在。