首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

C語言宏定義中 #、#@、## 三大運算符全解析:字符串化、字符化與 Token 拼接

     編輯:關於C

三大運算符速查

運算符名稱功能輸出類型
#字符串化將參數轉為字符串字面量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) ✅

實戰應用模式庫

1. 匿名變量生成(利用 __LINE__)


#define ANONYMOUS_VAR(type) type _anonymous_##__LINE__
// ANONYMOUS_VAR(int) → int _anonymous_42;

2. 結構體字段填充(枚舉值與字符串同步)


#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"}
};

3. 文件名記錄(調試定位)


#define _FILE_STR(f) #f
#define FILE_STR(f) _FILE_STR(f)
static const char *MY_FILE = FILE_STR(__FILE__); // 展開為當前文件名字符串

4. 數值類型緩衝區大小計算


#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 (含結束符)

5. Minix 風格斷言宏(標準庫級實現)


#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

#@ 字符化運算符補充

  • GCC/Clang 擴展,非標準 C。
  • #@x → 'x',參數必須為單字符 Token。
  • 標準 C 替代:#define TOCHAR(c) #c[0] 配合字符串化取首字符(笨拙)。
  • 現代代碼建議直接寫字符常量 'x',避免使用 #@。

最佳實踐總結

  1. 必須兩層宏:涉及 #/## 且參數可能為宏時,定義 _OP 和 OP 兩層。
  2. 連接結果合法性:## 產生的 Token 必須合法(標識符、常量、運算符),否則編譯錯誤。
  3. 字符串化空白處理:參數前後空白被保留,STR( a ) → " a ",通常無害。
  4. 避免副作用:宏參數在 ## 邊不展開,但其他位置會多次展開,SQUARE(x++) 導致雙增。
  5. 優先用內聯函數:類型安全、可調試、無展開陷阱。宏僅用於 __FILE__/__LINE__、Token 拼接、字符串化等語言機制無法替代的場景。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved