#define N 3
#define Y(n) ((N+1)*n)
printf("%d\n", 2*(N+Y(5+1))); // 結果是多少?
直覺計算:N=3,Y(6)=4*6=24,2*(3+24)=54。錯誤! 實際輸出 48。
宏處理是文本替換,非計算求值。預處理器按順序展開:
N → 3Y(n) → ((3+1)*n) → (4*n)2*(3+Y(5+1))Y(5+1) → 4*5+1 (注意:參數 5+1 直接替換 n,無括號保護)2*(3+4*5+1) = 2*(3+20+1) = 2*24 = 48#define Y(n) ((N+1)*n) 中,參數 n 在替換時未用括號包裹。調用 Y(5+1) 時,5+1 整體替換 n,導致 4*5+1 而非 4*(5+1)。
#define Y(n) ((N+1)*(n)) // 參數加括號
// 或更完善:
#define Y(n) ((N+1)*((n)))
現在 Y(5+1) → 4*(5+1) = 24,最終結果 54,符合預期。
| 規則 | 示例 | 說明 |
|---|---|---|
| 整體表達式加括號 | #define SQR(x) ((x)*(x)) | 防止優先級錯誤:SQR(a+b) → ((a+b)*(a+b)) |
| 每個參數加括號 | #define ADD(a,b) ((a)+(b)) | 防止 ADD(1,2)*3 → (1+2)*3 正確 |
| 多語句用 do-while(0) | #define DO() do{...}while(0) | 確保語句語義,配合分號使用 |
| 避免副作用參數 | 禁用 SQR(i++) | 參數多次展開導致多次求值 |
| 命名全大寫 | MAX_SIZE | 區分宏與函數,防衝突 |
// 字符串化 #
#define STR(x) #x
STR(hello) // → "hello"
// 拼接 ##
#define CONCAT(a,b) a##b
CONCAT(var, 1) // → var1
// 兩層展開技巧:強制參數先展開再字符串化
#define _STR(x) #x
#define STR(x) _STR(x)
#define VALUE 42
STR(VALUE) // → "42" 而非 "VALUE"
# 查看預處理後代碼
gcc -E source.c -o source.i
# 或
cpp source.c
遇到宏相關 Bug,首要動作查看 .i 文件,肉眼核對展開結果。
原則:能用函數/常量別用宏,宏僅用於編譯期配置、日誌位置捕獲、代碼生成等無替代場景。