C 編譯器在正式編譯前,會由預處理器對源碼進行文本級變換,主要經歷以下四個步驟:
將源文件字符集映射到執行字符集。歷史原因(早期鍵盤缺少特定符號)引入三字符序列替換:
| 三字符 | 替換為 |
|---|---|
| ??= | # |
| ??/ | \ |
| ??' | ^ |
| ??( | [ |
| ??) | ] |
| ??! | | |
| ??< | { |
| ??> | } |
| ??- | ~ |
現代環境極少使用,但預處理器仍保留此邏輯。
遇到 反斜線 + 換行符 組合時,刪除兩者,將物理行合併為邏輯行。常用於長宏定義或字符串換行:
printf("This is a very long string "\
"that spans multiple lines\n");
將所有註釋(`/* ... */` 和 `// ...`)替換為單個空格,保持行號不變,便於後續錯誤定位。
識別以 `#` 開頭、以換行符結尾的指令(受行拼接影響,指令可跨行)。核心指令包括:
關鍵規則:宏展開發生在標記層面,非文本簡單替換。參數兩側加括號、整體再加括號,防止運算符優先級錯誤:
#define SQUARE(x) ((x)*(x)) // 正確
// SQUARE(3+2) → ((3+2)*(3+2)) = 25
// 若寫成 #define SQUARE(x) x*x → 3+2*3+2 = 11 (錯誤)
| 操作符 | 功能 | 範例 |
|---|---|---|
# | 參數字符串化 | `#define STR(x) #x` → `STR(hello)` → `"hello"` |
## | 標記粘合 | `#define CONCAT(a,b) a##b` → `CONCAT(var,1)` → `var1` |
__VA_ARGS__ | 可變參數宏 | `#define LOG(fmt, ...) printf(fmt, __VA_ARGS__)` |
注意:`...` 必須是最後一個參數,`#define ERR(fmt, ..., code) ...` 為非法。
同一宏可重複定義(新定義覆蓋舊),作用域從定義處到文件結尾或遇到 `#undef`。條件編譯指令(`#ifdef`、`#ifndef`、`#if`、`#elif`、`#else`、`#endif`)配合宏實現代碼裁剪。
使用編譯器選項查看預處理結果:
輸出為純 C 代碼,無宏、無註釋、包含所有頭文件展開內容,是調試宏邏輯的利器。