宏使用之預先處理操作符總結

來源:互聯網
上載者:User
1.如何查看宏展開

      在開始之前,先簡單的介紹下,我們自己定義的一個宏,在引用的代碼中是如何被展開的。我用的是Microsoft Visual C++ 6.0,project menu -> settings,C/C++選項卡,在Project Options加上/P, 1 1所示。rebuild all,會產生與.cpp同名的.i檔案。開啟.i檔案,在這裡就可以看到代碼中宏是如何被展開的。

圖 1 1 添加“/P”編譯選項

比如:
#define CONS(a,b)    int(a##e##b)
int n = CONS(2, 3);    // 被展開為int n = int(2e3);即n = 2000;

2.預先處理操作符

      以前,我經常看到一些巧妙的宏定義,裡面有用到#、##、等符號,但不懂這些#是幹麼用的,今天突然想起這事,決定把它們徹底搞明白。
      開啟MSDN查看相關說明,如下表格所示,注意這些預先處理操作符只能用在#define的宏定義中

Preprocessor Operators Action
Stringizing operator (#) 字串化操作符,給對應的實參加上雙引號。
Charizing operator (#@) 字元化操作符,給對應的實參加上單引號。
Token-pasting operator (##) 符號串連操作符,串聯兩個符號。
2.1. Stringizing operator (#)

      字串化操作符,作用:將宏定義中的傳入參數名轉換成用一對雙引號括起來,即字串化。我們測試看看(樣本來自MSDN)。
#define stringer( x ) printf( #x "/n" )
void main()
{
    stringer( In quotes in the printf function call/n );
    stringer( "In quotes when printed to the screen"/n );  
    stringer( "This: /"  prints an escaped double quote" );
}
      用上面介紹的方法查看宏展開(在.i檔案中),以上代碼先行編譯時展開成如下代碼,可以發現用字串化(Stringizing)操作時,如果實參中有引號、反斜線字元時,宏展開時會在這些符號錢自動加上“/”以便將這些字元也字串化。
void main()
{
    printf( "In quotes in the printf function call/n" "/n" );
    printf( "/"In quotes when printed to the screen/"/n" "/n" );  
    printf( "/"This: ///"  prints an escaped double quote/"" "/n" );
}
最終輸入如下:
In quotes in the printf function call

"In quotes when printed to the screen"

"This: /"  prints an escaped double quote"
注意:
◆逸出字元
◇某些形式的傳入參數名中,若存在特殊字元,編譯器會自動為其添加逸出字元號'/'。

◆對空格的處理
◇忽略傳入參數名前面和後面的空格。
如:stringer(   ABC   );   // 宏展開為printf( "ABC" "/n" );
◇當傳入參數名間存在空格時,編譯器將會自動連接各個子字串,用每個子字串中只以一個空格串連,忽略其中多餘一個的空格。
如:stringer( ABC   DEF ); // 宏展開為printf( "ABC DEF" "/n" );

2.2. Charizing operator (#@)

      字元化操作符,作用:將宏定義中的傳入參數名轉換成用一對單引號括起來,即字元化。如:

#define MAKECHAR(ch)  #@ch

char a = MAKECHAR (b);  // 被宏展開為char a = 'b';
注意:
在預設的類型轉換中,傳入的參數如果不是字元型,會被截斷成char型。
如:
char a = MAKECHAR (abcd); 將會截斷成  a='d'。

2.3. Token-pasting operator (##)

      符號串連操作符,作用:將兩個符號串聯成一個符號使用。Token-Pasting Operator也成為merging operator,即合併作業。TEXT這個宏我想搞Windows編程的人都不陌生,我們看看它的定義:
#ifdef  UNICODE
#define __TEXT(quote) L##quote
#else
#define __TEXT(quote) quote
#endif

#define TEXT(quote) __TEXT(quote)
    用實際例子看看怎麼個串連法,在UNICODE環境中:
TEXT(ABC);  // 宏展開為LABC;
TEXT("ABC"); // 宏展開為L"ABC";
    再比如:
#define CONS(a,b)    int(a##e##b)
int n = CONS(2, 3);    // 被展開為int n = int(2e3);即n = 2000;

3.宏嵌套

      宏定義是允許嵌套的,但要注意:宏定義裡有用’#'、’#@’、'##'的地方宏參數是不會再展開
。為了說明問題,讓我們來看看一個簡單例子。

#define MAX_VAL 100
#define _STR(arg)  #arg
#define STR(arg)   _STR(arg)

int main()
{
    puts(_STR(MAX_VAL));
    puts(STR(MAX_VAL));
    return 0 ;
}
      先看看.i檔案中的宏展開是如何的:
int main()
{
    puts("MAX_VAL");
    puts("100");
    return 0 ;
}
輸出:
MAX_VAL
100
      從輸出結果就可以看出其差別所在,之所以會這樣,就是因為:宏定義裡有用’#'、’#@’、'##'的地方宏參數是不會再展開
。然而解決這個問題的方法很簡單,加多一層中間轉換宏,中間轉換宏的作用就是對宏參數進行宏展開。在許多大型項目、MFC代碼、微軟代碼的標頭檔中這樣的定義隨處可見。就像上面介紹的TEXT宏嵌套定義亦是如此。

4.總結

      宏的設計很巧妙,只要你能想得出,能大大的強化你的代碼,在學習和工作的過程中,要善於總結、歸納、收藏巧妙的宏應用。僅供參考,如有誤,請賜教。如果我寫的東西對你有用,不要忘了頂下。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.