# 與 ## 完整解析宏定義中 #(字串化運算子)與 ##(連接運算子)是進階巨集編程的核心工具,掌握其行為與展開順序至關重要。
# 字串化運算子將宏參數轉換為字串字面值:在替換後的參數兩側加上雙引號,並對特殊字元轉義。
#define WARN_IF(exp) if (exp) fprintf(stderr, "Warning: " #exp "\n") WARN_IF(x > 0); // 展開為:if (x > 0) fprintf(stderr, "Warning: x > 0\n");
## 連接運算子將兩個 Token 連接為單一 Token。可連接識別符、數字、運算子等任意預處理 Token。
#define CONCAT(a, b) a ## b #define MAKE_VAR(name, num) name ## num int CONCAT(var, 1) = 10; // int var1 = 10; int MAKE_VAR(temp, 42) = 20; // int temp42 = 20;
出現 # 或 ## 時,對應參數不會先展開,直接作為 Token 使用:
#define STR(s) #s #define VAL 100 STR(VAL); // 展開為 "VAL",而非 "100"!
加入中間層宏,利用「參數在替換列表中不帶 #/## 時會先展開」的規則:
#define _STR(s) #s
#define STR(s) _STR(s)
STR(VAL); // 1. STR(VAL) → _STR(VAL)
// 2. VAL 展開為 100
// 3. _STR(100) → "100" ✓
#include <iostream>
using namespace std;
// 測試 1:基本字串化
#define WARN_IF(exp) if (exp) fprintf(stderr, "Warning: " #exp "\n")
// 測試 2:基本連接
#define PASTER(n) printf("token" #n " = %d\n", token ## n)
// 測試 3:強制展開字串化
#define _STR(s) #s
#define STR(s) _STR(s)
// 測試 4:強制展開連接
#define _CONCAT(a, b) a ## b
#define CONCAT(a, b) _CONCAT(a, b)
// 測試 5:檔案名字串化
#define _FILE_NAME(f) #f
#define FILE_NAME(f) _FILE_NAME(f)
// 測試 6:型別大小字串化
#define _TYPE_SIZE(t) sizeof(#t)
#define TYPE_SIZE(t) _TYPE_SIZE(t)
// 測試 7:單字元字串化 (GCC 擴展)
#define CHAR_STR(c) #@c
int main() {
// 測試 1
int divider = 0;
WARN_IF(divider == 0); // Warning: divider == 0
// 測試 2
int token9 = 9;
PASTER(9); // token9 = 9
// 測試 3
cout << "INT_MAX = " << STR(INT_MAX) << endl; // INT_MAX = 2147483647
// 測試 4
int A = 15, B = 2;
cout << "A + B = " << CONCAT(A, B) << endl; // A + B = 17
// 測試 5
char fname[] = FILE_NAME(__FILE__);
cout << "File: " << fname << endl; // File: test.cpp
// 測試 6
cout << "Size of int: " << TYPE_SIZE(int) << endl; // Size of int: 4
// 測試 7
cout << CHAR_STR(@) << endl; // @
return 0;
}
# 用於除錯訊息、日誌、靜態斷言訊息## 用於自動生成識別符、構造名稱族群constexpr、模板、內聯函式