1. 程式人生 > >探索#和##的用法已經限制

探索#和##的用法已經限制

華麗的開頭:

在一些開源的軟體中經常出現#和##得用法, 因此搞清楚其語意就變得很重要,尤其是一些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,因此我們得出以下結論。

結論

   進行巨集操作的時候,如果一級巨集展開遇到#或者##,那麼即使此時的實參任然是巨集引數也不會進行巨集展開,而是直接進行(#格式化為字串,##直接連線到其他部分)操作,否則,將依次對內部巨集引數進行展開