▌第一次閱讀本系列的,可以先看:
▌前置處理器:
前置處理器,或稱預處理器,於編譯前進行。
例如常用的 #include
和 #define
,
#define
即 巨集/宏 (marco) 。
以 #
開頭都是前置處理器,其他的例子如
例如: #if
, #endif
, #else
, #elif
,
#ifdef
, #ifndef
, #error
, #pragma
, #typedef
等等。
詳情可參考 microsoft 的 C/C++ 前置處理器參考
例如常用的
#include
和 #define
,#define
即 巨集/宏 (marco) 。#
開頭都是前置處理器,其他的例子如例如:
#if
, #endif
, #else
, #elif
,#ifdef
, #ifndef
, #error
, #pragma
, #typedef
等等。
▌巨集 #define
:
巨集和指標可謂是 C 語言的精髓,
依靠它們可以創造無限可能,極為博大精深。
巨集可以節省工作量,同時增加可讀性,使代碼變得優雅。
即使巨集的本身不優雅、可讀。
依靠它們可以創造無限可能,極為博大精深。
即使巨集的本身不優雅、可讀。
▌#
:
#
— 字串化運算子,
放在巨集參數前,展開時中參數兩端加入 "
,使其字串化。
例子:
#define str(x) #x
使用 str( 9qwe7 h5d1v3jk6w )
會被展開為
" 9qwe7 h5d1v3jk6w "
另外,根據定義,
如果在字串常值中使用引數包含通常需要逸出序列時 (例如引號 (") 或反斜線 () 字元),必要的逸出反斜線字元就會自動插入至該字元之前。
#
會自動添加逸出反斜線字元(\)
利用此特性可以簡化大量需要逸出反斜線的字串。
例如 我要輸入 the "\" mean escaped character.
作為字串
正常: "the \"\\\" mean escaped character."
,
巨集: str(the "\" mean escaped character.)
。
某程度上來說,可讀性會較好。
還有常見的檔案路徑,如果我要輸入 D:\User\Documents\C_Test.txt
作為檔案路徑。
正常: "D:\\User\\Documents\\C_Test.txt"
巨集: str(D:\User\Documents\C_Test.txt)
。
可以不用考慮要不要加 \
,也可以打少一些字元。
#
— 字串化運算子,放在巨集參數前,展開時中參數兩端加入
"
,使其字串化。#define str(x) #x
str( 9qwe7 h5d1v3jk6w )
會被展開為" 9qwe7 h5d1v3jk6w "
如果在字串常值中使用引數包含通常需要逸出序列時 (例如引號 (") 或反斜線 () 字元),必要的逸出反斜線字元就會自動插入至該字元之前。
#
會自動添加逸出反斜線字元(\)利用此特性可以簡化大量需要逸出反斜線的字串。
the "\" mean escaped character.
作為字串正常:
"the \"\\\" mean escaped character."
,巨集:
str(the "\" mean escaped character.)
。某程度上來說,可讀性會較好。
D:\User\Documents\C_Test.txt
作為檔案路徑。正常:
"D:\\User\\Documents\\C_Test.txt"
巨集:
str(D:\User\Documents\C_Test.txt)
。可以不用考慮要不要加
\
,也可以打少一些字元。
▌##
:
##
— 語彙基元帶入的運算子,
這名字是 microsoft 改的,有時稱為「合併」運算子。
可以合併文字,例子:
#define mix(x) abc##x
使用 mix(d)
會被展開為 abcd
意義何在呢?因為識別字是以空格作為分隔,若果
#define mix(x) abcx
前置處理器無法辨認出 x
,它只看到有 abcx
這個識別字。
加入 ##
可以令它識別出 巨集參數 並將其合併。
另外, ##
前後的空格可有可無,
即 abc##x
等價 abc ## x
##
— 語彙基元帶入的運算子,這名字是 microsoft 改的,有時稱為「合併」運算子。
#define mix(x) abc##x
mix(d)
會被展開為 abcd
#define mix(x) abcx
x
,它只看到有 abcx
這個識別字。加入
##
可以令它識別出 巨集參數 並將其合併。##
前後的空格可有可無,即
abc##x
等價 abc ## x
▌阻止另一個巨集的展開:
一旦在使用了 #
或 ##
在 巨集中,
其巨集參數是 是另一個 巨集/宏 ,
將會阻止另一個 巨集/宏 的展開。
例子:
#define foo abc
#define foo2(x) x##123
foo2(foo)
的結果並不會是 abc123
,
不會展開作為參數的巨集 foo
,
所以結果是 foo123
。
可參看 C宏展开的几个注意事项 - nanoix9 - 博客园,
沒仔細看,貌似很詳細。
#
或 ##
在 巨集中,其巨集參數是 是另一個 巨集/宏 ,
將會阻止另一個 巨集/宏 的展開。
#define foo abc
#define foo2(x) x##123
foo2(foo)
的結果並不會是 abc123
,不會展開作為參數的巨集
foo
,所以結果是
foo123
。沒仔細看,貌似很詳細。
▌預先定義的巨集:
有一些預先定義在前置處理器中的巨集:
__LINE__
:從原始檔開頭起算, __LINE__
所在的行數
__FILE__
:檔案名稱(字串常數)
__DATE__
:以 Mmm dd yyyy 格式(eg. Oct 31 2018),表示編譯時的日期
__TIME__
:以 hh:mm:ss 格式,表示編譯時的時間 (字串常數)
__STDC__
:若 1 ,表示編譯器遵循 ISO C 標準
__STDC_VERSION__
:若支援 C99,數值為 199901L
, 若支援 C11,數值為 201112L
。
__func__
:函式名稱 (C99)
...還有很多,不全部列出。
__LINE__
和 __FILE__
常用於錯誤訊息。
__func__
也是,不過是 C99 才支援。
__LINE__
:從原始檔開頭起算, __LINE__
所在的行數__FILE__
:檔案名稱(字串常數)__DATE__
:以 Mmm dd yyyy 格式(eg. Oct 31 2018),表示編譯時的日期__TIME__
:以 hh:mm:ss 格式,表示編譯時的時間 (字串常數)__STDC__
:若 1 ,表示編譯器遵循 ISO C 標準__STDC_VERSION__
:若支援 C99,數值為 199901L
, 若支援 C11,數值為 201112L
。__func__
:函式名稱 (C99)__LINE__
和 __FILE__
常用於錯誤訊息。__func__
也是,不過是 C99 才支援。最後特意推薦 宏定义的黑魔法 - 宏菜鸟起飞手册,
循序漸進,很好,很詳細,很深入。
學到不少新技術,
推薦推薦~!
學到不少新技術,
推薦推薦~!
▌參考資料:
預處理器 - 維基百科,自由的百科全書
https://zh.wikipedia.org/zh-hk/%E9%A2%84%E5%A4%84%E7%90%86%E5%99%A8
C/C++ 前置處理器參考
https://msdn.microsoft.com/zh-tw/library/y4skk93w.aspx
前置處理器運算子
https://msdn.microsoft.com/zh-tw/library/wy090hkc.aspx
C宏展开的几个注意事项 - nanoix9 - 博客园
http://www.cnblogs.com/aquastone/p/c-macro-expansion.html
C 語言程式設計教學:前置處理器 (Preprocessor) | Michael Chen 的技術文件分享
https://cwchen.tw/c-prog/preprocessor/
宏定义的黑魔法 - 宏菜鸟起飞手册
https://onevcat.com/2014/01/black-magic-in-macro/
https://zh.wikipedia.org/zh-hk/%E9%A2%84%E5%A4%84%E7%90%86%E5%99%A8
C/C++ 前置處理器參考
https://msdn.microsoft.com/zh-tw/library/y4skk93w.aspx
前置處理器運算子
https://msdn.microsoft.com/zh-tw/library/wy090hkc.aspx
C宏展开的几个注意事项 - nanoix9 - 博客园
http://www.cnblogs.com/aquastone/p/c-macro-expansion.html
https://cwchen.tw/c-prog/preprocessor/
https://onevcat.com/2014/01/black-magic-in-macro/
0 Comments
發佈留言