1. 程式人生 > >C語言巨集與單井號(#)和雙井號(##)

C語言巨集與單井號(#)和雙井號(##)

C(和C++)中的巨集(Macro)屬於編譯器預處理的範疇,屬於編譯器概念(而非執行期概念)。下面對常遇到的巨集的使用問題做了簡單總結。

關於#和##

在C語言的巨集中,#的功能是將其後面的巨集引數進行字串化操作(Stringfication),簡單說就是在對它所引用的巨集變數通過替換後在其左右各加上一個雙引號。比如下面程式碼中的巨集:

#define WARN_IF(EXP) /
    do { if (EXP) /
        fprintf(stderr, "Warning:" #EXP "/n"); } /
    while(0)

 那麼在實際使用中會出現下面所示的替換過程:

WARN_IF(divider == 0);

被替換為:

do {
    if (divider == 0)
        fprintf(stderr, "Warning" "divider == 0" "/n");
} while(0);

   這樣每次divider(除數)為0的時候便會在標準錯誤流上輸出一個提示資訊。

   而##被稱為連線符(concatenator),用來將兩個Token連線為一個Token。注意這裡連線的物件是Token就行,而不一定是巨集的變數。比如你要做一個選單項命令名和函式指標組成的結構體的陣列,並且希望在函式名和選單項命令名之間有直觀的、名字上的關係。那麼下面的程式碼就非常實用:

struct command 
{
    char *name;
    void (*function)(void);
};
#define COMMAND(NAME) {NAME, NAME ## _command }

  //然後你就用一些預先定義好的命令來方便的初始化一個command結構的陣列了:

struct command commands[] = {
    COMMAND(quit);
    COMMAND(help);
    ...
}

    COMMAND巨集在這裡充當一個程式碼生成器的作用,這樣可以在一定程度上減少程式碼密度,間接地也可以減少不留心所造成的錯誤。我們還可以n個##符號連結n+1個Token,這個特性也是#符號所不具備的。比如:

#define LINK_MULTIPLE(a,b,c,d) a##_##b##_##c##_##d

typedef struct _record_type LINK_MULTIPLE(name, company, position, salary);

//這裡這個語句將展開為:

  //typedef struct _record_type name_company_position_salary;

  ##連線符號由兩個#號組成,其功能是在帶引數的巨集定義中將兩個子串(token)連線起來,從而形成一個新的子串。但它不可以是第一個或者最後一個子串。所謂的子串(token)就是指編譯器能夠識別的最下語法單元。

  #符是把傳遞過來的引數當成字串進行替代。

  下面看看它們是怎麼工作的。這是MSDN上的一個例子。假設程式中已經定義了這樣一個帶引數的巨集:

#define paster(n) printf("token" #n "=%d", token##n)

  同時又定義了一個整形變數:int token9 = 9;

  現在在主程式中以下面的方式呼叫這個巨集: paster(9);

   那麼在編譯時,上面的這句話被擴充套件為:printf("token" "9" "=%d", token9);

   注意到這個例子中,paster(9);中的這個"9"被原封不動的當成了一個字串,與"token"連線在了一起,從而成為了token9。而#n也被"9"所替代。可想而知,上面程式執行的結果就是在螢幕上打印出token9=9