宏展開小結

在C++中,儘管使用宏是不推薦的,但是很多時候宏也會爲我們提供一些方便,且在現有代碼中宏也確實大量存在。有人認爲宏是錯誤的根源,因爲宏展開後,有可能會出現一些稀奇古怪的代碼,那麼,本文在這裏總結一些查看宏展開後的代碼的方法。

1、在visual studio 2005中,選擇項目屬性-〉配置屬性-〉C/C++-〉預處理器,在“生成預處理文件”中選擇生成預處理文件,“不帶行號(/EP /P)”或“帶行號(/P)”,那麼就會在項目所在文件夾下生成一個與源文件同名的“.i”文件,該文件爲展開宏以後的代碼文件。

vs宏展開選項設置
具體設置情況如上圖,宏展開後的情況如下所示

原始的代碼macro.cpp

 

  1. #include <iostream>  
  2. #include <cstdio>  
  3.   
  4. #define  TO_STRING_MACRO(x)     #x  
  5. #define  A_TESTING_MACRO(n)     printf("a testing macro with a int argument n=%d !/n", n)   
  6. #define  SHOW_MACRO_CONTENT(m)  TO_STRING_MACRO(m)   
  7.   
  8. using namespace std;   
  9.   
  10. int main(int argc ,char* argv[] )  
  11. {  
  12.        int a = 10;  
  13.        int *b = &a;  
  14.   
  15.        cout << TO_STRING_MACRO(a) << endl;  
  16.        cout << TO_STRING_MACRO(10 + 20) << endl;  
  17.        cout << A_TESTING_MACRO(10 + 20) << endl;  
  18.        cout << TO_STRING_MACRO(A_TESTING_MACRO(a)) << endl;  
  19.        cout << SHOW_MACRO_CONTENT(A_TESTING_MACRO(a)) << endl;  
  20.        cout << SHOW_MACRO_CONTENT(A_TESTING_MACRO(*b)) << endl;   
  21.   
  22.        *b = 100;  
  23.        A_TESTING_MACRO(*b);         
  24.   
  25.        return 0;  
  26. }  
  

 

宏展開後的代碼macro.i

 

  1. using namespace std;   
  2.   
  3. int main(int argc ,char* argv[] )  
  4. {  
  5.        int a = 10;  
  6.        int *b = &a;  
  7.   
  8.        cout << "a" << endl;  
  9.        cout << "10 + 20" << endl;  
  10.        cout << printf("a testing macro with a int argument n=%d !/n", 10 + 20) << endl;  
  11.        cout << "A_TESTING_MACRO(a)" << endl;  
  12.        cout << "printf(/"a testing macro with a int argument n=%d !//n/", a)" << endl;  
  13.        cout << "printf(/"a testing macro with a int argument n=%d !//n/", *b)" << endl;   
  14.   
  15.        *b = 100;  
  16.        printf("a testing macro with a int argument n=%d !/n", *b);         
  17.   
  18.        return 0;  
  19. }  
    

 

2、在linux下,使用gcc,g++編譯器編譯,使用編譯器的-P選項,假設有源文件macro.cpp,則具體命令如下

 

  1. $g++ -E -P macro.cpp > macro.prescan  
  
    

 

這裏要說一下宏的展開次序,如果宏有參數,如TO_STRING_MACRO(x)中的x,我們稱之爲形參,而宏實際的參數我們稱之爲實參,如TO_STRING_MACRO(a)中的a。

宏的展開是個很複雜的過程,但可以用以下三步來簡單描述,

首先用實參替換形參,將實參代入宏文本中;

然後如果實參也是宏,則展開實參;

最後再繼續處理宏替換後的宏文本,若宏文本也包含宏則繼續展開,否則完成展開。

但是有個例外,那就是第一步後,將實參代入宏文本後,實參之前如果遇到字符“#”或“##”,即使實參是宏,也不再展開實參,而是當作文本處理。

以宏TO_STRING_MACRO爲例,我們簡要分析一下該宏的展開次序。

#define  TO_STRING_MACRO(x)     #x

TO_STRING_MACRO(a),在展開時,首先將實參a替換到宏的內容中,替換後爲“#a”,該宏將實參a轉換爲了文本。

如果宏的實參也是一個宏,如TO_STRING_MACRO(A_TESTING_MACRO(a)),則首先展開爲“#A_TESTING_MACRO(a)”,宏A_TESTING_MACRO(a)之前爲字符“#”,則不再進行展開,而是轉換爲文本。所以這樣是不能夠輸出宏“A_TESTING_MACRO(a)”展開後的內容的。

爲了輸出宏A_TESTING_MACRO(a)展開後的內容,我們可以定義這樣一個宏

#define  SHOW_MACRO_CONTENT(m)  TO_STRING_MACRO(m)

如果要輸出一個宏的內容,可以按如下方式使用該宏進行輸出

SHOW_MACRO_CONTENT(A_TESTING_MACRO(a))

下面解析一下該宏的展開順序:

首先,將實參A_TESTING_MACRO(a)代入宏文本TO_STRING_MACRO(m)中,得到TO_STRING_MACRO(A_TESTING_MACRO(a)),注意,這裏還沒有遇到“#”,所以繼續展開實參,即宏A_TESTING_MACRO(a),該宏全部展開後,得到的內容如下TO_STRING_MACRO(printf("a testing macro with a int argument n=%d !/n", a))

接下來,展開宏TO_STRING_MACRO,即得到了一個字符串,該字符串就是宏A_TESTING_MACRO展開後的內容。

如果以上沒有描述清楚整個過程,則可以看看下面各個步驟處理後的內容來幫助理解

SHOW_MACRO_CONTENT(A_TESTING_MACRO(a))

TO_STRING_MACRO(A_TESTING_MACRO(a))

TO_STRING_MACRO(printf("a testing macro with a int argument n=%d !/n", a))

#printf("a testing macro with a int argument n=%d !/n", a)

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