雙井號操作符速查
| 操作符 | 名稱 | 功能 | 示例 |
# | Stringification | 參數 → 字符串字面量 | #x → "x" |
## | Token Pasting | 兩標記合並為一標記 | a##b → ab |
經典應用場景
1. 自動生成命令表(字符串化 + 粘連)
void quit_cmd() { puts("quit"); }
void help_cmd() { puts("help"); }
#define CMD_ENTRY(name) { #name, name##_cmd }
struct cmd { const char *name; void (*fn)(); } cmds[] = {
CMD_ENTRY(quit),
CMD_ENTRY(help),
};
/* 展開後:
{ "quit", quit_cmd },
{ "help", help_cmd }
*/
2. 調試打印宏(變量名 + 值)
#define PRINT_VAR(x) printf(#x " = %d\n", x)
int token9 = 9;
PRINT_VAR(token9); // 展開:printf("token9" " = %d\n", token9); → token9 = 9
3. 避免宏參數副作用的陷阱
#define SQUARE(x) ((x)*(x)) // 參數加括號!
SQUARE(i++); // 正確:((i++)*(i++)) → 仍有副作用,建議改用 inline 函數
預處理器調試技巧
gcc -E -P source.c # 僅輸出預處理結果,無行號標記
clang -Xclang -macro-expand # 交互式宏展開
標準庫中的實際應用
assert(expr) → #expr 保留表達式文本
offsetof(type, member) → 結合 ## 計算偏移
- X-Macros:列表宏 + 多次包含生成枚舉/數組/switch