CWKSC's blog | 博客

宏的 # 和 ## 運算子、瑣碎規則和預先定義的宏

Featured image

目錄: C/C++ Macro 宏系列 | CWKSC’s blog | 博客

這一篇來說一些宏的基礎知識、###、一些瑣碎規則和預先定義的宏

▌預處理器

預處理器,負責一些編譯前進行的工作

在 C/C++ 中,一般以 # 開頭的都與預處理器有關

例如: #include, #define, #if, #endif, #else, #elif, #ifdef, #ifndef, #error, #pragma, #typedef 等等

詳情可參考 Microsoft Docs 的 C/C++ preprocessor referencePreprocessor Directives

# 字符串化運算子 (Stringizing operator)

與宏參數配合使用,放在宏參數前面,展開時會在參數兩端加入 ",使其字串化

#define str(x) #x

str(42)  // "42"

# 和參數之間的空白可有可無

#define str(x) #       x

前後的空白會被忽略

str(   42   )  // "42"

中間的空白會被壓縮為一個

str(  42   42  )  // "42 42"

註解會被忽略並變為一個空白

str(42/**/42) // "42 42"

會自動添加逸出反斜線字元,對特殊字符進行轉義

利用此特性可以簡化大量需要逸出反斜線的字串

str(the " \ " mean escaped character.)
// "the \" \\ \" mean escaped character."

另外還有常見的檔案路徑,如果我要輸入 D:\User\Documents\C_Test.txt 作為檔案路徑

正常:"D:\\User\\Documents\\C_Test.txt"

宏: str(D:\User\Documents\C_Test.txt)

可以不用考慮要不要加 \ ,也可以打少一些字元

## Token 粘貼運算子 (Token-pasting operator)

把兩個 Token 合併為一個

#define merge(a, b) a##b

merge(4, 2) // 42

意義何在?因為識別字 (identifier) 是以空格等等作為分隔

以下的結果,顯然不是我們想要的

#define merge2(a, b) ab
#define merge3(a, b) a b

merge2(4, 2) // ab
merge3(4, 2) // 4 2

## 前後的空格可有可無

#define merge4(a, b) a   ##   b

merge4(4, 2) // 42

生成的 Token 可用於進一步的宏替換

#define another_marco 42

merge(another, _marco) // [1]
another ## _marco      // [2] 
another_marco          // [3] 處理 ## 完成,準備重掃描
42                     // [4] 重掃描完成

### 阻止另一個宏的展開

在使用了 ### 的宏中,其作用的宏參數是另一個宏,這將會阻止另一個宏的展開

#define another_marco 42

#define str(x) #x
#define merge(a, b) a##b

str(another_marco) // "another_marco"
merge(another_marco, _meow) // another_marco_meow

把另一個宏作爲參數時,### 不會把它展開

#define another_marco 42
#define merge5(a, b) a a ## b

merge5(another_marco, _meow)
another_marco another_marco ## _meow
another_marco another_marco_meow
42 another_marco_meow

不是它作用的宏參數不會受影響

▌預先定義的宏 (Predefined macros)

__FILE__ 以字符串常量展開為當前輸入文件的名稱,這是預處理器打開文件的路徑

__LINE__ 以十進制整數常量展開為當前行號

__DATE__ 當前源文件的編譯日期,格式是 Mmm dd yyyy 的恆長字符串常量

__TIME__ 預處理翻譯單元的翻譯時間,格式為 hh:mm:ss 的字符串常量

__STDC__ 以十進制整數常量展開,若 1 ,表示編譯器遵循 ISO C 標準,否則,它將變為 0

__STDC_VERSION__ 擴展為C Standard的版本號,其形式為一個長整數常量,若支援 C99,數值為 199901L , 若支援 C11,數值為 201112L 。若支援 C17,數值為 201710L

__func__ 函式名稱,展開為字符串常量

還有很多其他的宏,可參考 Predefined macros | Microsoft DocsPredefined Macros (The C Preprocessor)

__LINE____FILE__ 常用於錯誤訊息,__func__ 也是,不過 C99 才支援

還有一些很實用編譯器擴展,但 C 標準沒有支持,例如 __COUNT__ ,從零開始,每次調用數值加一

▌總結

# 字符串化運算子 (Stringizing operator)

## Token 粘貼運算子 (Token-pasting operator)

在使用了 ### 的宏中,其作用的宏參數是另一個宏,將會阻止另一個宏展開

CWKSC

Author 作者

CWKSC

喜歡編程,會一點點鋼琴,會一點點畫畫,喜歡使用顏文字 About me 關於我

For any comments or discussions on my blog post, you can open an issue here

對於我博客文章的任何評論或討論,可以在這裏開一個 issue

Feel free to give your comments. OW<