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

C/C++ 宏運算子 # 與 ## 完整解析:字串化、連接與多層展開技巧

     編輯:關於C++

C/C++ 宏運算子 # 與 ## 完整解析

宏定義中 #(字串化運算子)與 ##(連接運算子)是進階巨集編程的核心工具,掌握其行為與展開順序至關重要。

# 字串化運算子

將宏參數轉換為字串字面值:在替換後的參數兩側加上雙引號,並對特殊字元轉義。

#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、模板、內聯函式
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved