在 C++ 宏編程中,# 和 ## 是兩個極其強大但常被誤解的前處理運算符。掌握它們的行為細節,對於編寫靈活、健壯的巨集至關重要。
C++ 編譯器會在預處理階段自動合併相鄰的字串字面量,無論中間是否有空白字符:
const char* p1 = "Hello," "World!"; // 等價於 "Hello,World!"
const char* p2 = "Hello," "World!"; // 多個空白同理
const char* p3 = "Hello,""World!"; // 無空白也合併
const char* p4 = "Hello,World!"; // 完整字串
// 以上四種寫法編譯後完全等價,常量段僅存一份 "Hello,World!"
這一特性是理解 # 運算符行為的基礎。
作用:將宏參數不經擴展直接轉換為字串字面量。
關鍵規則:
" 或 \ 等特殊字符時,會自動添加轉義字符 \經典範例(來自 MSDN):
#define F abc
#define B def
#define FB(arg) #arg
#define FB1(arg) FB(arg)
FB(F B) // 展開為 "F B" —— 參數未擴展,直接字串化
FB1(F B) // 展開為 "abc def" —— 參數先擴展為 "abc def",再字串化
預處理器輸出驗證:
"F B";
"abc def";
作用:將多個預處理標記連接成單一標記。
關鍵規則:
實戰範例:
#define paster(n) printf_s("token" #n " = %d", token##n)
int token9 = 9;
paster(9);
預處理展開過程:
paster(9);
printf_s("token" #9 " = %d", token##9);
printf_s("token" "9" " = %d", token9);
printf_s("token9 = %d", token9);
# 時注意參數擴展時機,善用雙層宏強制擴展## 連接後若產生非法標記會導致編譯錯誤,需謹慎設計