__FILE__ 源檔案的名稱 如XXX.cpp
__LINE__ 代碼在源檔案中是第幾行
__DATE__ 源檔案完成日期如Eec 11 2012
__TIME__ 源檔案完成時間如21:35:35
__TIMESTAMP__ 源檔案完成日期時間如Tue Dec 11 21:35:35 2012
如分配記憶體函數malloc()的一個版本就使用了上面的宏
#define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
另外可以在程式中使用__FILE__和__LINE__來確定是哪個檔案哪一行出錯:
char *pszFileName = (char*)malloc(MAX_PATH * sizeof(char));
if (pszFileName == NULL)
printf("Error in %s %d\n", __FILE__, __LINE__);
當然實際程式中大多用__FILE__和__LINE__快速定位錯誤後不會直接輸出,而是通過另一程式來將這些資訊以EMAIL形式反饋給開發人員。
對__FILE__可以方便的轉化成wchar_t類型,MSDN就有這個例子:
#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
#define __WFILE__ WIDEN(__FILE__)
wchar_t *pwsz = __WFILE__;
宏中的#的功能是將其後面的宏參數進行字串化操作(Stringizing operator),簡單說就是在它引用的宏變數的左右各加上一個雙引號。
如定義好#define STRING(x) #x之後,下面二條語句就等價。
char *pChar = "hello";
char *pChar = STRING(hello);
還有一個#@是加單引號(Charizing Operator)
#define makechar(x) #@x
char ch = makechar(b);與char ch = 'b';等價。
但有小問題要注意,宏中遇到#或##時就不會再展開宏中嵌套的宏了。什麼意思了?比如使用char *pChar = STRING(__FILE__);雖然__FILE__本身也是一個宏,但編譯器不會展開它,所以pChar將指向"__FILE__"而不是你要想的形如"D:\XXX.cpp"的源檔案名稱。因此要加一個中間轉換宏,先將__FILE__解析成"D:\XXX.cpp"字串。
定義如下所示二個宏:
#define _STRING(x) #x
#define STRING(x) _STRING(x)
再調用下面語句將輸出帶""的源檔案路徑
char* pChar = STRING(__FILE__);
printf("%s %s\n", pChar, __FILE__);
可以比較下STRING(__FILE__)與__FILE__的不同,前將帶雙引號,後一個沒有雙引號。
再講下##的功能,它可以拼接符號(Token-pasting operator)。
MSDN上有個例子:
#define paster( n ) printf( "token"#n" = %d\n", token##n )
int token9 = 100;
再調用 paster(9);宏展開後token##n直接合并變成了token9。整個語句變成了
printf( "token""9"" = %d", token9 );
在C語言中字串中的二個相連的雙引號會被自動忽略,於是上句等同於
printf("token9 = %d", token9);。
即輸出token9 = 100
有了上面的基礎後再來看樣本1
#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
#define __WFILE__ WIDEN(__FILE__)
wchar_t *pwsz = __WFILE__;
第一個宏中的L是將ANSI字串轉化成unicode字串。如:wchar_t *pStr = L"hello";
再來看wchar_t *pwsz = __WFILE__;
__WFILE__被首先展開成WIDEN(__FILE__),再展開成WIDEN2("__FILE__表示的字串"),再拼接成 L"__FILE__表示的字串" 即L"D:\XXX.cpp" 從而得到unicode字串並取字串地址賦值給pwsz指標。
在VC中_T(),TEXT ()也是用的這種技術。
在tchar.h標頭檔中可以找到:
#define _T(x) __T(x)
#define __T(x) L ## x
在winnt.h標頭檔中可以找到
#define TEXT(quote) __TEXT(quote) // r_winnt
#define __TEXT(quote) L##quote // r_winnt
因此不難理解為什麼第三條語句會出錯error C2065: 'LszText' : undeclared identifier
wprintf(TEXT("%s %s\n"), _T("hello"), TEXT("hello"));
char szText[] = "hello";
wprintf(TEXT("%s %s\n"), _T(szText), TEXT(szText));
而將"hello"定義成宏後就能正確運行。
#define SZTEXT "hello"
wprintf(TEXT("%s %s\n"), _T(SZTEXT), TEXT(SZTEXT));
註:由於VC6.0預設是ANSI編碼,因此要先設定成unicode編碼,在project菜單中選擇Setting,再在C/C++標籤對話方塊中的Category中選擇Preprocessor。再地Preprocessor definitions編輯框中將_MBCS去掉,加上_UNICODE,UNICODE。