2010-12-06 21 views
14
#include <stdio.h> 
    #define f(a,b) a##b 
    #define g(a) #a 
    #define h(a) g(a) 

    int main() 
    { 
    printf("%s\n",h(f(1,2))); 
    printf("%s\n",g(f(1,2))); 
    return 0; 
    } 

僅僅通過查看程序,「可能」期望輸出爲,對於這兩個printf語句都是相同的。但是在運行程序時,你會得到如下結果:宏中的#和##

bash$ ./a.out 
12 
f(1,2) 
bash$ 

這是爲什麼?

回答

18

因爲這就是預處理器的工作原理。

單個'#'將從給定參數創建一個字符串,而不管該參數包含什麼,而雙'##'將通過連接參數來創建一個新的標記。

如果您想更好地瞭解如何評估宏,請嘗試查看預處理的輸出(例如使用gcc -E)。

13

函數式宏中的參數發生除非它是###的操作數,否則在將其替換並重新掃描以進一步擴展之前展開。由於g的參數#的操作數,參數未擴展,而是立即被串行化("f(1,2)")。因爲h的參數不是#也不##操作數,該參數被第一膨脹(12),則取代(g(12)),然後重新掃描併發生進一步擴展("12")。

4

下面是一些相關的概念,你的問題:

Argument Prescan

宏參數是完全宏擴展他們 取代成宏體,除非他們是stringified粘貼 與其他令牌。在替換之後,包括替換參數的整個宏體 被再次掃描宏被擴展爲 。結果是參數被掃描兩次以在其中擴展 宏調用。

Stringification

當宏參數用於具有前導「#」,預處理器 與實際參數的文字文本替換它,轉換爲 一個字符串常量

Token Pasting/Token Concatenation

同時擴大 宏這是兩個符號合併成一個常常是有用的。這就是所謂的標記粘貼令牌串聯。 '##' 預處理運算符執行令牌粘貼。當宏擴展爲 時,每個'##'運算符兩側的兩個令牌合併爲一個令牌,然後在宏擴展中替換'##'和兩個原始令牌。

所以您的方案的具體過程是這樣的:

h(f(1,2)) 
-> h(12) // f(1,2) pre-expanded since there's no # or ## in macro h 
-> g(12) // h expanded to g 
12 // g expanded 

g(f(1,2)) 
-> "f(1,2)" //f(1,2) is literally strigified because of the `#` in macro g. f(1,2) is NOT expanded at all.