iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 21
0
自我挑戰組

30 Days 如何把 C 語言偽裝成高階語言 OWO /系列 第 22

Day 21:重溫前置處理器、巨集( #, ## )、預先定義的巨集

▌第一次閱讀本系列的,可以先看:

本系列的大綱 傳送門


▌前置處理器:

前置處理器,或稱預處理器,於編譯前進行。
例如常用的 #include#define
#define巨集/宏 (marco) 。

# 開頭都是前置處理器,其他的例子如
例如: #if, #endif, #else, #elif,
#ifdef, #ifndef, #error, #pragma, #typedef 等等。

詳情可參考 microsoft 的 C/C++ 前置處理器參考


▌巨集 #define

巨集指標可謂是 C 語言的精髓
依靠它們可以創造無限可能,極為博大精深。

巨集可以節省工作量,同時增加可讀性,使代碼變得優雅
即使巨集的本身不優雅、可讀。

基礎知識就不說了,以下講述 ### 前置處理器運算子的功能,
詳情亦可參考 microsoft 的 前置處理器運算子

#:

#字串化運算子
放在巨集參數前,展開時中參數兩端加入 ",使其字串化。

例子:

#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


▌阻止另一個巨集的展開:

一旦在使用了 ### 在 巨集中,
其巨集參數是 是另一個 巨集/宏 ,
將會阻止另一個 巨集/宏 的展開。

例子:

#define foo abc
#define foo2(x) x##123

foo2(foo) 的結果並不會是 abc123
不會展開作為參數的巨集 foo
所以結果是 foo123

可參看 C宏展开的几个注意事项 - nanoix9 - 博客园
沒仔細看,貌似很詳細。


回來看了一下,經過測試,我發現這篇文章,C宏展开的几个注意事项 - nanoix9 - 博客园中,
【这个时候,逗号视为合法分隔符。】的部分好像是錯的。
不知道,注意一下,可能是我的編譯器跟他不同。
...

= = 超久之後回來了...
原來 VS(Visual Studio) 把這種附有逗號的展開視為整體,單個參數。
所以 【这个时候,逗号视为合法分隔符。】的部分好像是錯的。在 gcc 下正確的。
VS 下錯誤。


▌預先定義的巨集:

有一些預先定義在前置處理器中的巨集:
__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/


上一篇
Days 20: 垃圾回收器系列:記憶體碎片化、內存池
下一篇
Day 22:重溫 可變參數函數、可變參數宏 __VA_ARGS__
系列文
30 Days 如何把 C 語言偽裝成高階語言 OWO /31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言