宏看似簡單,實則暗藏殺機。本文針對六大高頻錯誤模式(括號缺失、重複定義、危險重定義、`#`/`##` 語義、分號濫用、參數展開順序)逐一拆解,並附帶一道經典面試題深度解析預處理機制。
// 錯誤
#define CAL(x,y) x+y
CAL(2,4)*CAL(2,5) // → 2+4*2+5 = 15 (預期 6*7=42)
// 正確:參數、整體雙層括號
#define CAL(x,y) ((x)+(y))
#define PI 3.14
#define PI 10.48
// 程序中 PI = 10.48
// 建議:#ifndef PI #define PI 3.14 #endif
#define int char // 徹底破壞類型系統
#define break continue // 邏輯反轉
// 絕對禁止!
#define SUM(a,b) printf("a add b = %d", a+b)
SUM(1,2) // 輸出: a add b = 3 (字面量 a,b)
#define SUM2(a,b) printf(#a " add " #b " = %d", a+b)
SUM2(1,2) // 輸出: 1 add 2 = 3
#define NAME(x,y) x##y
NAME(Steve, Johnson) // → SteveJohnson
#define SUM(a,b) ((a)+(b));
SUM(a1,b1) + SUM(a2,b2) // → ((a1)+(b1));+((a2)+(b2)) 語法錯誤
// 正確:#define SUM(a,b) ((a)+(b))
#define num 10
#define f(x) #x
#define g(x) f(x)
int main() {
printf("f(num)=%s g(num)=%s\n", f(num), g(num));
}
// 輸出: f(num)=num g(num)=10
// 原理:
// f(num) → #num → "num" (# 阻斷 num 展開)
// g(num) → f(num) → f(10) → #10 → "10" (g 先展開參數,再進 f 時參數已是 10)
**口訣**:`#`/`##` 遇到參數**不再遞歸展開**;需展開時加中轉層 (`#define STR(s) _STR(s)`)。
gcc -E macro.c -o macro.i // 查看展開後代碼
// 或
gcc -E macro.c > a.txt
宏是雙刃劍,精通預處理機制,才能駕馭它而不被它傷害!