在 C 語言巨集定義中,# 與 ## 是兩個極具威力但常被忽視的預處理運算符。它們分別負責「字串化」與「標記粘合」,善用這兩個特性能讓程式碼更靈活、除錯更高效,甚至實現泛型編程的雛形。本文將透過實例深入剖析這兩個運算符的運作機制與實戰應用。
# 運算符只能出現在帶參數巨集的替換列表中,其功能是將對應的實際參數「原樣」轉換為字串字面值。這在除錯輸出時極為實用。
#define PRINT_INT(x) printf(#x " = %d\n", x)
當呼叫 PRINT_INT(i / j); 時,預處理器會將其展開為:
printf("i / j" " = %d\n", i / j);
由於 C 語言會自動合併相鄰的字串字面值,最終等價於:
printf("i / j = %d\n", i / j);
這樣一來,輸出結果會自動攜帶運算式本身作為標籤(例如 i / j = 3),免去了手寫格式字串的麻煩,且不易出錯。
## 運算符能將兩個標記(如識別碼)黏合成單一標記。若操作數包含巨集參數,粘合動作會在參數被實際引數取代之後發生。
#define MK_ID(n) i##n
呼叫 MK_ID(1)、MK_ID(2)、MK_ID(3) 經預處理後分別產生識別碼 i1、i2、i3。以下宣告:
int MK_ID(1), MK_ID(2), MK_ID(3);
將展開為:
int i1, i2, i3;
傳統 MAX 巨集在參數含副作用時會失效;寫多個同名函數又違反 C 語言規則。利用 ## 可依類型自動產生不同函數名稱:
#define GENERIC_MAX(type) \
type type##_max(type x, type y) \
{ \
return x > y ? x : y; \
}
呼叫 GENERIC_MAX(float) 即展開為:
float float_max(float x, float y) { return x > y ? x : y; }
同理可產生 int_max、double_max 等,避免重複撰寫邏輯,達到「寫一次、多型別複用」的效果。
善用這兩個運算符,能讓巨集從簡單的文字替換進化為強大的程式碼產生器,大幅提升開發效率與程式碼可維護性。