#define中 #與##的神奇用法

http://zjf30366.blog.163.com/blog/static/411164582009061075923/

#define f(a,b) a##b 
#define d(a) #a 
#define s(a) d(a) 

void main( void ) 

    puts(d(f(a,b))); 
    puts(s(f(a,b))); 


輸出結果: 
f(a,b) 
ab

分析:  ##把兩個符號連起來 
    #a指把a當成符號,就是把#後面的看成字符串


# 和 ## 操作符是和#define宏使用的. 使用# 使在#後的首個參數返回爲一個帶引號的字符串. 例如, 命令 
    #define to_string( s ) # s 
將會使編譯器把以下命令 
    cout < < to_string( Hello World! ) < < endl; 
理解爲 
    cout < < "Hello World!" < < endl; 
使用##連結##前後的內容. 例如, 命令 
    #define concatenate( x, y ) x ## y 
    ... 
    int xy = 10; 
    ... 
將會使編譯器把 
    cout < < concatenate( x, y ) < < endl; 
解釋爲 
    cout < < xy < < endl; 
理所當然,將會在標準輸出處顯示'10'.

 

puts(d(f(a,b)));  ----> 因爲d宏中的參數是另外一個宏,且帶##,所以作爲參數的宏不展開,相當於 
                            puts(#f(a,b));----->puts("f(a,b)"); 
puts(s(f(a,b))); ----> 因爲s宏中的參數是另外一個宏,但不帶##,所以作爲參數的宏先展開,相當於 
                            puts(s(ab));----->puts(d(ab));---->puts(#ab);---->puts("ab");

 

#define f(a,b) a##b 
#define d(a) #a --》 以"#"開頭的,直接替換,不展開:immediately replaced by the unexpanded actual argument 
#define s(a) d(a) --》 非以"#"開頭的,先展開,再替換,也就是一般的情況 

所以就兩種情況: 
1,不以"#"開頭的,先展開參數a,然後是替換代碼:puts(s(f(a,b)));-->puts(s(ab))-->puts(d(ab))-->puts("ab") 
2,以"#"開頭的,直接替換,不展開:puts(d(f(a,b)))-->puts("f(a,b)")

 

#include <stdio.h>
#define DIRECT_LITERAL(a)  #a
#define INDIRECT_LITERAL(a) DIRECT_LITERAL(a)

 

int main(void)
{
    puts(DIRECT_LITERAL(INDIRECT_LITERAL(a + b)));
    puts(INDIRECT_LITERAL(DIRECT_LITERAL(a + b)));
    return 0;

這其實從編譯角度的展開歸約也可以理解啊。 

以上代碼第一種情況,當預編譯器看到DIRECT_LITERAL後查到它是宏定義,定義爲#a,此時後面的參數部分就會以#a的形式生成到源文件中。也就是說,預編譯後的源文件中,替代第一條語句的就是: 
puts("INDIRECT_LITERAL(a + b)");輸出則是INDIRECT_LITERAL(a + b) 

而對於第二條語句,當預編譯器看到INDIRECT_LITERAL後查到它是宏定義,定義爲DIRECT_LITERAL(a),這時先把它作爲DIRECT_LITERAL(DIRECT_LITERAL(a + b))的形式暫存起來,你也可以理解爲這個狀態是語法樹的當中一個葉結點。然後再分析後面的DIRECT_LITERAL後面的參數部分,即:DIRECT_LITERAL(a + b),同樣,預編譯器會將它歸約爲"a + b"的形式。這樣對於裏面的DIRECT_LITERAL(a + b)的形式就完全確定下來了,那麼這個值就可以充當葉子結點,即它底下不會再有結點。然後再回到剛纔那個狀態,DIRECT_LITERAL("a + b")最後就是"\"a + b\""。所以這裏輸出是"a + b"。 

值得注意的是#a是將參數a轉爲字符串形式。所以像DIRECT_LITERAL(a)的展開形式是字符串常量"a" 
那麼DIRECT_LITERAL("a")展開就是"\"a\""。 

#include <stdio.h>
#define DIRECT_LITERAL(a)  #a
#define INDIRECT_LITERAL(a) DIRECT_LITERAL(a)

#define DIRECT_CAT(a, b)   a##b
#define INDIRECT_CAT(a, b)  DIRECT_CAT(a, b)

int main(void)
{
    puts(DIRECT_LITERAL(INDIRECT_LITERAL(a + b)));
    puts(INDIRECT_LITERAL(DIRECT_LITERAL(a + b)));
    puts(INDIRECT_LITERAL(DIRECT_CAT(INDIRECT_CAT(a, b), INDIRECT_CAT(c, d))));
    puts(INDIRECT_LITERAL(INDIRECT_CAT(DIRECT_CAT(a, b), DIRECT_CAT(c, d))));
    return 0;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章