2011年9月3日 星期六

C/C++ 的預處理定義 : # , #@ , ##

 C/C++ 的預處理定義:Stringizing Operator (#) , Charizing Operator (#@) , Token-Pasting Operator (##)

(這是ANSI C/C++ 定義的, 並不在傳統C/C++中)


一  Stringizing Operator (#)cc++中數位標誌符#被賦予了新的意義,即字串化操作符。其作用是:將巨集定義中的傳入參數名轉換成用一對雙引號括起來
參數名字串。其只能用於有傳入參數的巨集定義中,且必須置於巨集定義體中的參數名前。
如:
#define example(instr)  printf("the input string is:\t%s\n",#instr)
#define example1(instr) #instr
當使用該巨集定義時:
example(abc)
; 在編譯時將會展開成:printf("the input string is:\t%s\n","abc");
string str=example1(abc)
; 將會展成:string str="abc"


注意:
1
對空格的處理
   a.
忽略傳入參數名前面和後面的空格。
      如:str=example1(   abc ); 將會被擴展成 str="abc"
  b.
當傳入參數名間存在空格時,編譯器將會自動連接各個子字串,用每個子字串中只以一個空格連接,忽略其中多餘一個的空格。
     如:str=exapme( abc    def); 將會被擴展成 str="abc def"


2 轉義字元
   a.
某些形式的傳入參數名中,若存在特殊字元,編譯器會自動為其添加轉義字元號'\'
     如:
         string str=example1("escap'\e");
相當於:str="\"escap\'\\e\"";
   b. vc6.0
vc7.0並不能正確的解析所有需要特殊字元的情況。此時會給出錯誤報告:error C2001: 常數中有換行符。

     如:
           example1(abc\'); //
此處報警 error C2001: 常數中有換行符

Charizing Operator (#@)字元化操作符。其同樣只能用於有傳入參數的巨集定義中,且必須置於巨集定義體中的參數名前。作用,將傳的單字元參數名轉換成字元,以一對單引用括起來。
如:
#define exampleChar(inchar) #@inchar
使用該巨集定義:
char a=exampleChar(a);
將會被擴展成:char a='b';

注意:
vc6.0vc7.0中默認的類型轉換中,可以將int截斷成char。因此,參數名中最多不能超過4個字元。
如:
char a=example(abcd)
將會截斷成 a='d'。同時編譯器會給出:warning C4305: “=” : “int”“char”截斷

Token-Pasting Operator (##)##:符號連接操作符。
巨集定義中:參數名,即為形參,如#define sum(a,b) (a+b);中ab均為某一參數的代表符號,即形式參數。
##的作用則是將巨集定義的多個形參成一個實際參數名。
如:
#define exampleNum(n) num##n
int num9=9;
使用:
int num=exampleNum(9);
將會擴展成 int num=num9;

注意:
1.當用##連接形參時,##前後的空格可有可無。
如:#define exampleNum(n) num ## n 相當於 #define exampleNum(n) num##n
2.
連接後的實際參數名,必須為實際存在的參數名或是編譯器已知的巨集定義。

問題:
#include

#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;
}

輸出:

12
f(1,2)
為啥?
解答:
首先我們要知道,使用#可以把巨集引數變為一個字串,使用##可以把兩個巨集引數貼合在一起

但是當巨集引數是另一個巨集的時候,凡巨集定義裏有用'#''##'的地方的巨集引數不會再展開。

如題目,

g(f(1,2))中,f(1,2)當作g的參數時被字串化了,所以它不再以宏的形式展開。

h(f(1,2))中,f(1,2)當作h的參數,再h自身被展開之前,f(1,2)必須完全的宏展開。

因此,當h(x)g(x)來代替時,f(1,2)已經完全展開成12

下面這段話說明瞭一切:

According to the Argument Prescan rule, Macro arguments are completely macro-expanded before they are substituted into a macro body, unless they are stringified or pasted with other tokens. After substitution, the entire macro body, including the substituted arguments, is scanned again for macros to be expanded. The result is that the arguments are scanned twice to expand macro calls in them.

沒有留言:

張貼留言

注意:只有此網誌的成員可以留言。