探索#和##的用法已經限制
華麗的開頭:
在一些開源的軟體中經常出現#和##得用法, 因此搞清楚其語意就變得很重要,尤其是一些C語言功底並不是那麼紮實的童鞋來說(比如我)。其實#和##的作用可以統一用一句話來概括,"使得變數的組織和運用變得更加靈活", 有些讀者可能剛開始無法理解,沒關係,通過本文,相信您對#和##一定有一個深入的認識。好了,進入正題。
#的用法
解釋:一般#是將右側巨集引數格式化為一個字串
例項1:
#define T(X) #X int main(void) { printf("T(x):\"%s\"\r\n", T(A)); printf("T(x):\"%s\"\r\n", T("A")); printf("T(x):\"%s\"\r\n", T('A')); while (true); return 0; }
大家猜猜列印的結果是什麼,此處冥想10秒鐘。。。。。。如下
其輸出結果為
可以看出 :不管A是字串 或者是 數字 再或者 沒有定義, #X的結果都將是將X轉換為字串"X"
哪有的人就要問了,要是A被定義了,但是A是一個巨集,那還會不會如上一樣直接轉換為"A"呢? 我們繼續實驗
實驗2
#define A 129
#define T(X) #X
int main(void)
{
printf("T(x):\"%s\"\r\n", T(A));
while (true);
return 0;
}
這次的結果
我們可以看到,結果依舊是"A",而不是"129" ,這是不是說明#在做巨集展開的時候並不會展開其內部的巨集引數呢。然而並不是都是這樣,這僅僅侷限於一次巨集展開。
例項3
#define A (129)
#define T(X) #X
#define TT(X) T(X)
int main(void)
{
printf("T(x):\"%s\"\r\n", TT(A));
while (true);
return 0;
}
按上面的結論,此時結果應該依舊是"A",然而並不是這麼簡單的。
這是為什麼呢?讓我們來看一看預編譯時的巨集展開過程。
TT(A) ---> T(A) ----> T((129)) ---> #(129) --> "(129)"
T(A) ---- #A ---- > "A"
此時我們可以得出結論:
#只會將巨集引數直接格式化為字串,如果一級展開發現其含有# 那麼此時將不會對巨集進行展開,直接轉為字串,如果不存在且其實參任然是巨集引數,則對內部巨集引數進行替換。
##的用法
解釋:##一般用來連線兩個特定的物件,並組成一個新的字串或者變數,也就是將兩個物件粘合在一次。
大家是不是看到這個解釋都被繞迷糊了,沒關係,下面會給出一些簡單的例項程式碼進行解釋。
例項1:
#define GPIOA 0X10
#define RCC_APB2Periph_GPIOA 0X0101
#define RCC_CLK(X) RCC_APB2Periph_##X
int main(void)
{
printf("0X%X\r\n", RCC_CLK(GPIOA));
while (1);
return 0;
}
相信大家都猜到了結果,沒錯 結果就是0X0101,這說明##的引數格式化過程中同樣不存在引數的展開,如果結果必然無法執行通過,因為其試圖列印RCC_APB2Periph_0X10,這顯然是錯誤的。
同樣,按照上面的思維,因為##處在以及巨集展開的位置,是不是由於這個原因導致的巨集引數沒有被展開呢?帶著這樣的疑惑,我們繼續進行探索實驗。
例項2
#include <stdio.h>
#define GPIOA 0X10
#define RCC_APB2Periph_GPIOA 0X0101
#define RCC_CLK(X) RCC_APB2Periph_##X
#define RCC_CLK_COPY(X) RCC_CLK(X)
int main(void)
{
printf("0X%X\r\n", RCC_CLK_COPY(GPIOA));
while (1);
return 0;
}
猜測:
1、如果巨集引數一直不會展開,那麼此時列印的結果應該與上面的一樣 為0X0101
2、如果巨集引數展開了,那麼此時必定是報RCC_APB2Periph_0X10未定義什麼的
好了 我們直接看結果
結果完全符合了猜想2,因此我們得出以下結論。
結論
進行巨集操作的時候,如果一級巨集展開遇到#或者##,那麼即使此時的實參任然是巨集引數也不會進行巨集展開,而是直接進行(#格式化為字串,##直接連線到其他部分)操作,否則,將依次對內部巨集引數進行展開