| 運算符 | 名稱 | 功能 | 輸出類型 |
|---|---|---|---|
# | 字符串化 | 將參數轉為字符串字面量 | const char* |
#@ | 字符化 | 將參數轉為字符常量 | char (GCC 擴展) |
## | 連接/粘合 | 拼接兩側 Token 為單一 Token | 任意合法 Token |
#include
#define STR(s) #s
#define TOCHAR(c) #@c
#define CONCAT(a,b) int(a##e##b)
int main() {
printf(STR(pele)); // "pele"
printf("%c\n", TOCHAR(z)); // 'z'
printf("%d\n", CONCAT(2,3)); // 2e3 → 2000 (科學計數法)
return 0;
}
#define A (2)
#define STR(s) #s
#define CONCAT(a,b) a##b
STR(INT_MAX) // → "INT_MAX" (未展開為 "2147483647")
CONCAT(A, A) // → AA (未展開為 (2)(2) 或 22)
規則:參數出現在 # 或 ## 旁邊時,不進行宏展開,直接作為 Token 處理。
#define A (2)
// 字符串化兩層
#define _STR(s) #s
#define STR(s) _STR(s) // 先展開參數,再字符串化
// 連接兩層
#define _CONCAT(a,b) a##b
#define CONCAT(a,b) _CONCAT(a,b)
STR(INT_MAX) // → STR(2147483647) → _STR(2147483647) → "2147483647" ✅
CONCAT(A, A) // → CONCAT((2), (2)) → _CONCAT((2), (2)) → (2)(2) ✅
#define ANONYMOUS_VAR(type) type _anonymous_##__LINE__
// ANONYMOUS_VAR(int) → int _anonymous_42;
#define FILL(name) {name, #name}
enum Action { OPEN, CLOSE };
struct MapEntry { Action act; const char *str; };
struct MapEntry table[] = {
FILL(OPEN), // {OPEN, "OPEN"}
FILL(CLOSE) // {CLOSE, "CLOSE"}
};
#define _FILE_STR(f) #f
#define FILE_STR(f) _FILE_STR(f)
static const char *MY_FILE = FILE_STR(__FILE__); // 展開為當前文件名字符串
#define _TYPE_BUF_SIZE(t) sizeof(#t)
#define TYPE_BUF_SIZE(t) _TYPE_BUF_SIZE(t)
char buf[TYPE_BUF_SIZE(INT_MAX)];
// → _TYPE_BUF_SIZE(2147483647)
// → sizeof("2147483647")
// → 11 (含結束符)
#ifdef _ANSI
#define __str(x) #x
#define __xstr(x) __str(x) // 兩層展開 __FILE__ __LINE__
#define assert(expr) ((expr) ? (void)0 : \
__bad_assertion("Assertion \"" #expr "\" failed, file " \
__xstr(__FILE__) ", line " __xstr(__LINE__) "\n"))
#endif
#@x → 'x',參數必須為單字符 Token。#define TOCHAR(c) #c[0] 配合字符串化取首字符(笨拙)。'x',避免使用 #@。#/## 且參數可能為宏時,定義 _OP 和 OP 兩層。## 產生的 Token 必須合法(標識符、常量、運算符),否則編譯錯誤。STR( a ) → " a ",通常無害。## 邊不展開,但其他位置會多次展開,SQUARE(x++) 導致雙增。__FILE__/__LINE__、Token 拼接、字符串化等語言機制無法替代的場景。