天天看點

#define簡單妙用

1:__FILE__,__LINE__,宏展開,中間層宏

__FILE__是編譯器預定義宏,表示此檔案的絕對路徑,但是ASCII字元。

假如你想将__FILE__轉換成寬字元。

或許你想這樣

#define __WFILE__  L##__FILE__

wchar_t pszFilePath[]=__WFILE__;

這樣的話編譯器會提示說,“L__FILE__”: 未聲明的辨別符。

這是為什麼?

如果##後的參數本身也是一個宏的話,##會阻止這個宏的展開,也就是隻替換一次。需要加一層中介完成代換,就想這樣。

#define __WFILE3__(x) (L##x)

#define __WFILE2__(x) __WFILE3__(x)

#define __WFILE__  __WFILE2__(__FILE__)

當宏函數的定義中調用了另一個宏函數,預處理器會将其中所有可展開的宏展開。這樣就可以了。

再看一個具有迷惑性的錯誤宏定義:

#define __WFILE1__(x) (L##x)

#define __WFILE__  __WFILE1__(__FILE__)

這樣呢,編譯器同樣會報出L__FILE__未定義,也就是說L_FILE__不是一個字元串。

讓我們一步一步看看預處理器是如何做的吧?

1.當預處理器到達wchar_t pszFilePath[]=__WFILE__;它發現宏定義,然後執行展開__WFILE1__( __FILE__ )

2.同時發現__FILE1__又是一個宏定義,那麼展開(L##__FILE1__)

為什麼預處理器沒有展開__FILE1__呢,你可能覺得,自己已經添加了中間步驟。但實際上沒有,我們單單看__WFILE1__( __FILE__ ),不關其他定義,你就會發現,這個宏定義根據前面的規則是絕對不會展開的,是以前面的__WFILE__根本沒有起到中間代換的作用。

是以總結起來的話,從__FILE__第一次出現,必須要經過中間一層宏。中間一層宏是指__FILE__必須出現在宏聲明中(宏的左側),同時宏的定義(即宏定義右側)也是一個宏。那麼這個宏才能稱為中間層。這個中間層才能起到展開__FILE__的作用。

下面看下個錯誤的字元串宏定義例子

#define SHOWLINENUMW3(x) (L##x)

#define SHOWLINENUMW2(x) SHOWLINENUMW3(#x)

#define SHOWLINENUMW(x)  SHOWLINENUMW2(x)

#define __WLINE__ SHOWLINENUMW(__LINE__)

#define __WFILE__  __WFILE2__(__FILE__ )

#define __N__ (__WFILE__ __WLINE__)

TCHAR szFileName[]=__N__;

注意L##x是帶括号的。編譯器提示:項不會計算為接受 1 個參數的函數。

為什麼會出錯呢?那是因為對多個單獨的字元串連接配接時,不能用括号括起來括起任意一個。當然單獨一個可以。

如:

wchar_t szTemp[]=(L"nihao"); //第一種 可以

wchar_t szTemp[]=(L"nihao") (L"tahao");//第二種 不可以。

wchar_t szTemp[]=L"nihao" L"woyehao";//第三種 可以

而前面給出的錯誤的例子經過宏替換這後就相當于第二種,是以編譯器會報告錯誤。我們需要将(L##x)改成L##x才能正确運作。

是以正确的例子是:

#define SHOWLINENUMW3(x) L##x

#define __WFILE3__(x) L##x

2:__VA_ARGS__

__VA_ARGS__好像是c99标準加進來的宏定義功能,可能用于不變參數宏

#define MY_PRINTF( x,...) printf( x, ##__VA_ARGS__ )

其中...表示可變參數清單,__VA_ARGS__則是表示前面的可變參數清單,其中##用于在沒可變參數清單時去掉前面的逗号。編譯器會識别出##,并去掉前面的逗号。

如果沒有##:

#define MY_PRINTF( x,...) printf( x, __VA_ARGS__ )

MY_PRINTF( "Hello World" );

宏展開後:

printf( "Hello World", );

這樣的編譯器會抱怨警告,甚至編譯不過。采用##之後就可以了。而且##本身也是連接配接符。

當然有時我們可以直接:

#define MY_PRINTF(...) printf(__VA_ARGS__)

此時就不需要##,因為前面沒有逗号。

暫時總結自己知道的這麼多的,以後再補充吧。

繼續閱讀