VC 宏與預處理使用方法總結(轉)


目錄(?)[-]

  1. C/C++ 預定義宏^
    1. C/C++ 預定義宏用途:診斷與調試輸出^
  2. CRT 和 C 標准庫中的宏^
    1. NULL 空指針^
    2. limits.h 整數類型常量^
    3. float.h 浮點類型常量^
    4. math.h 數學常量^
    5. EOF 常量^
    6. errno.h 錯誤代碼^
    7. locale 類別^
    8. _MAX_PATH 等文件名與路徑長度限制^
    9. RAND_MAX 隨機數最大值^
    10. va_arg/va_start/va_end 訪問變長函數參數^
    11. 宏實現的 CRT 函數^
  3. Microsoft 預定義宏^
    1. 平台與系統類^
    2. 版本號類^
    3. 工程配置管理類^
    4. 輔助類^
  4. Windows API 中的注釋性宏^
  5. Windows API 中的常用宏^
    1. 類型輔助類^
    2. GDI 類^
    3. 錯誤處理類^
    4. 調用規范類^
    5. 國際化類^
    6. 資源類^
    7. 網絡類^
  6. 字符串化操作符 #^
    1. 用 # 操作構造字符串化宏 STRINGIZE^
    2. CRT 中的 STRINGIZE 定義^
    3. STRINGIZE 的展開規則^
  7. 拼接操作符 ##^
  8. TCHAR 統一字符類型和處理^
    1. _TCHAR, _TEXT()/_T(), _t 系列函數^
    2. TCHAR, LPTSTR/LPCTSTR, TEXT(), A/W 版本 Windows API^
  9. 宏的缺點和替代方法^
    1. 宏難於調試^
    2. 宏的使用缺陷^
    3. 宏造成全局名字空間污染^
    4. 優先使用宏的情況^
  10. 條件編譯^
  11. 預編譯頭文件^
    1. 使用 PCH 的編譯命令^
  12. 常用預處理指令^
    1. #error 產生人工編譯錯誤^
    2. #line 改變行號和源文件名^
    3. # 空指令^
  13. #pragma 預處理指令^
    1. #pragma once 只包含一次頭文件^
    2. #pragma message 編譯時輸出消息^
    3. #pragma push_macro/pop_macro 保存和恢復宏定義^
    4. #pragma warning 禁用和啟用編譯警告^
    5. #pragma comment 目標文件注釋和編譯選項傳遞^
    6. #pragma 區段操作^
    7. #pragma pack 設置成員字節對齊^
    8. #pragma inline 函數設置^
    9. #pragma 優化指令^
    10. #pragma deprecated 聲明廢棄函數^
    11. #pragma omp 使用 OpenMP 指令^
    12. #pragma region/endregion 折疊代碼塊^
    13. #pragma setlocale 設置源代碼中字符串字面量的編碼^
    14. #pragma include_alias 定義頭文件別名^
  14. 預處理相關編譯選項^
    1. /D 定義宏^
    2. /E, /EP, /P 預處理選項^
    3. /showIncludes 輸出頭文件列表^

作者:Breaker Zhao
轉載請注明作者和原文鏈接

VC 2005 中的宏 (#define) 與預處理 (#if/#ifdef/#pragma) 的使用方法總結。

關鍵字:宏, 預定義宏, 預處理, 預編譯頭, VC, #pragma, 編譯選項, 程序區段

目錄


VC 中的宏使用方法參考 MSDN: Macros (C/C++)

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">C/C++ 預定義宏^

__LINE__: 當前源文件的行號,整數
__FILE__: 當前源文件名,char 字符串,使用 /FC 選項產生全路徑
__DATE__: 當前編譯日期,char 字符串,格式 Aug 28 2011
__TIME__: 當前編譯時間,char 字符串,格式 06:43:59
__STDC__: 整數 1,表示兼容 ANSI/ISO C 標准,配合 #if 使用
__TIMESTAMP__: 最后一次修改當前文件的時間戳,char 字符串,格式 Sun Aug 28 06:43:57 2011
__cplusplus: 以 C++ 方式而非 C 語言方式編譯時定義,VC 2005 中定義為 199711L,配合 #ifdef 使用

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:C/C++ 預定義宏的取值^

  1. // MacroTest.h  
  2. void PrintSourceInfo()  
  3. {  
  4.     const _TCHAR* pszstdc;  
  5.     const _TCHAR* pszcpp;  
  6.   
  7. #if __STDC__  
  8.     pszstdc = _T("YES");  
  9. #else  
  10.     pszstdc = _T("NO");  
  11. #endif  
  12.   
  13. #ifdef __cplusplus  
  14.     pszcpp = _T("YES");  
  15. #else  
  16.     pszcpp = _T("NO");  
  17. #endif  
  18.   
  19.     _tprintf(_T("File: %s, Line: %d, Date: %s, Time: %s, Timestamp: %s, ANSI/ISO C: %s, C++: %s\n"),  
  20.              _T(__FILE__), __LINE__, _T(__DATE__), _T(__TIME__), _T(__TIMESTAMP__), pszstdc, pszcpp);  
  21. }  
  22.   
  23. // 宏化的 PrintSourceInfo()  
  24. #define PRINT_SOURCE_INFO() \  
  25.     _tprintf(_T("File: %s, Line: %d, Date: %s, Time: %s, Timestamp: %s\n"), \  
  26.              _T(__FILE__), __LINE__, _T(__DATE__), _T(__TIME__), _T(__TIMESTAMP__));  

MacroTest.h 中定義函數 PrintSourceInfo() 和 PRINT_SOURCE_INFO(),在 MacroTest.cpp include=> MacroTest.h,並調用它們

輸出結果

(1). 使用函數 PrintSourceInfo(),無論 Debug/Release 方式編譯,無論是否 inline 化 PrintSourceInfo(),輸出結果相同,均是 MacroTest.h 的信息:

File: d:\source\macrotest\macrotest.h, Line: 64, Date: Aug 28 2011, Time: 06:43:59, Timestamp: Sun Aug 28 06:43:57 2011, ANSI/ISO C: NO, C++: YES 

(2). 使用宏 PRINT_SOURCE_INFO(),Debug/Release 方式編譯輸出結果大致相同,均是 MacroTest.cpp 的信息,只是 Debug 輸出的 __FILE__ 是全路徑,而 Release 輸出的是相對路徑:

File: d:\source\macrotest\macrotest.cpp, Line: 14, Date: Aug 28 2011, Time: 07:42:30, Timestamp: Sun Aug 28 07:38:25 2011 

說明

(1). __FILE__、__DATE__、__TIME__ 是 char 字符串,而不是 wchar_t 寬字符字符串,需配合 _T()、_t 系列函數使用

(2). 如果在函數 PrintSourceInfo() 中使用宏,則 __FILE__、__LINE__、__TIME__ 等表示的是 PrintSourceInfo() 所在文件,即例 1 中的 MacroTest.h 的信息;如果在宏 PRINT_SOURCE_INFO() 中使用宏,因為宏 PRINT_SOURCE_INFO() 嵌套展開的緣故,__FILE__ 等表示的是 PRINT_SOURCE_INFO() 展開所在文件,即 MacroTest.cpp 的信息

(3). 無論使用 PrintSourceInfo() 還是 PRINT_SOURCE_INFO(),__LINE__ 總是文件 .h/.cpp 的固有行號,而非 [MacroTest.cpp include=> MacroTest.h] 預處理展開后的行號

(4). 在 VC 2005 中,上述編譯方式下沒有定義 __STDC__,要使 __STDC__ = 1,應同時滿足以下條件:

  • (a). 以 C 方式編譯
  • (b). 使用編譯選項 /Za,表示禁止 Microsoft C/C++ 語言擴展,從而兼容 ANSI C/C++

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">C/C++ 預定義宏用途:診斷與調試輸出^

參考 VC CRT 和 MFC 的代碼,注意:需要在宏中使用 __FILE__、__LINE__,原因見上面“說明 (2)”

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">CRT 的診斷與調試輸出:assert, _ASSERT/_ASSERTE, _RPTn/_RPTFn/_RPTWn/_RPTFWn^

CRT 的診斷宏 assert()、_ASSERT()/_ASSERTE()

  1. // assert.h  
  2.   
  3. _CRTIMP void __cdecl _wassert(__in_z const wchar_t * _Message, __in_z const wchar_t *_File, __in unsigned _Line);  
  4.   
  5. #define assert(_Expression) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) )  
  6.   
  7. // crtdbg.h  
  8.   
  9. #define _ASSERT_EXPR(expr, msg) \  
  10.         (void) ((!!(expr)) || \  
  11.                 (1 != _CrtDbgReportW(_CRT_ASSERT, _CRT_WIDE(__FILE__), __LINE__, NULL, msg)) || \  
  12.                 (_CrtDbgBreak(), 0))  
  13.   
  14. #ifndef _ASSERT  
  15. #define _ASSERT(expr)   _ASSERT_EXPR((expr), NULL)  
  16. #endif  
  17.   
  18. #ifndef _ASSERTE  
  19. #define _ASSERTE(expr)  _ASSERT_EXPR((expr), _CRT_WIDE(#expr))  
  20. #endif  

CRT 的調試輸出宏 _RPTn()/_RPTFn(),n: 0 ~ 5
_RPTWn()/_RPTFWn() 是寬字符版

  1. // crtdbg.h  
  2.   
  3. #define _RPT_BASE(args) \  
  4.         (void) ((1 != _CrtDbgReport args) || \  
  5.                 (_CrtDbgBreak(), 0))  
  6.   
  7. #define _RPTF0(rptno, msg) \  
  8.         _RPT_BASE((rptno, __FILE__, __LINE__, NULL, "%s", msg))  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">MFC 的診斷與調試輸出:ASSERT/VERIFY, ASSERT_VALID, TRACE/TRACEn^

MFC 的診斷宏 ASSERT()/VERIFY()、ASSERT_VALID()

  1. // afx.h  
  2.   
  3. #define ASSERT(f)          DEBUG_ONLY((void) ((f) || !::AfxAssertFailedLine(THIS_FILE, __LINE__) || (AfxDebugBreak(), 0)))  
  4. #define ASSERT_VALID(pOb)  DEBUG_ONLY((::AfxAssertValidObject(pOb, THIS_FILE, __LINE__)))  

MFC 的調試輸出宏 TRACE()/TRACEn(),n: 0 ~ 3

  1. // atltrace.h  
  2.   
  3. #ifndef ATLTRACE  
  4. #define ATLTRACE ATL::CTraceFileAndLineInfo(__FILE__, __LINE__)  
  5. #define ATLTRACE2 ATLTRACE  
  6. #endif  
  7.   
  8. // afx.h  
  9.   
  10. #include <atltrace.h>  
  11. #define TRACE ATLTRACE  
  12.   
  13. #define THIS_FILE          __FILE__  
  14. #define VERIFY(f)          ASSERT(f)  
  15. #define DEBUG_ONLY(f)      (f)  
  16.   
  17. #define TRACE0(sz)              TRACE(_T("%s"), _T(sz))  
  18. #define TRACE1(sz, p1)          TRACE(_T(sz), p1)  
  19. #define TRACE2(sz, p1, p2)      TRACE(_T(sz), p1, p2)  
  20. #define TRACE3(sz, p1, p2, p3)  TRACE(_T(sz), p1, p2, p3)  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">MFC 的調試版 new^

  1. // afx.h  
  2.   
  3. void* AFX_CDECL operator new(size_t nSize, LPCSTR lpszFileName, int nLine);  
  4. #define DEBUG_NEW new(THIS_FILE, __LINE__)  
  5.   
  6. // 用戶代碼  
  7.   
  8. // 調試版 new  
  9. #ifdef _DEBUG  
  10. #define new DEBUG_NEW  
  11. #endif  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">CRT 和 C 標准庫中的宏^

VC CRT 和 C 標准庫中的宏參考 MSDN: Global Constants

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">NULL 空指針^

NULL 在 stddef.h, stdio.h, stdlib.h 等多個頭文件中定義,是地址/指針類型的 0,如下:

  1. #ifdef __cplusplus  
  2. #define NULL    0  
  3. #else  
  4. #define NULL    ((void *)0)  
  5. #endif  

C++ 中的 0 是類型自動的,所以用 0 定義 NULL;而 C 中 0 是確定的 int 類型,所以需要強制

C++ 中,當 NULL 的相關操作數,如:對比操作 ptr == NULL,或函數的形參是指針類型時,或者能夠“從指針類型隱式轉換”時,0 被自動轉換為指針類型

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:NULL 隱式轉換和 0 是類型自動的^

  1. // baby pointer wrapper  
  2. class Pointer  
  3. {  
  4. public:  
  5.     // 非 explicit 構造函數,說明 Pointer 可以從指針類型 void* 隱式轉換  
  6.     Pointer(void* p) : m_Ptr(p)  
  7.     {}  
  8.   
  9.     bool IsNull() const  
  10.     {  
  11.         return (m_Ptr == NULL);  
  12.     }  
  13.   
  14. private:  
  15.     void*    m_Ptr;  
  16. };  
  17.   
  18. // 形參可以從指針類型 void* 隱式轉換  
  19. void TestPointer(Pointer ptr)  
  20. {  
  21.     _tprintf(_T("ptr is %sNULL\n"), ptr.IsNull() ? _T("") : _T("NOT "));  
  22. }  
  23.   
  24. // 用戶代碼  
  25. TestPointer(0);         // OK,0 是類型自動的,0 被自動轉換為 void*,再次隱式轉換為 Pointer  
  26. TestPointer(NULL);      // OK,NULL 就是 0,同上  
  27. TestPointer(1);         // Error,C++ 中 1 不同於 0,它是確定的 int 類型,  
  28.                         // 只能提升轉換到 float/double 類型,不能自動轉換為指針  
  29. TestPointer((int*)1);   // OK,強制轉換 1 為 int*,int* 自動轉換為 void*,再次隱式轉換為 Pointer  
  30.                         // 注意:void* 到 int* 不能自動轉換,需要強制,參考 malloc() 的返回值  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">limits.h 整數類型常量^

在 limits.h 中定義,定義了各種 int 類型 (unsigned, char, short, long, __int64) 的最小、最大值,如 SCHAR_MAX (signed char MAX)、UCHAR_MAX (unsigned char MAX)、USHRT_MAX (unsigned short MAX) 等。編譯時,如果 int 字面量超出這些范圍,會編譯出錯

參考 MSDN: Integer Limits

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">float.h 浮點類型常量^

在 float.h 中定義,定義各種浮點類型 (float, double, long double) 的極限值,如最小、最大值,最小浮點差量 (epsilon) 等

參考 MSDN: Floating Limits

例子:浮點數極限值:判斷浮點數是否相等^

  1. // 對比一個 double 是否為 0  
  2. inline  
  3. bool double_equal0(double n)  
  4. {  
  5.     return (n >= 0 ? n < DBL_MIN : n > -DBL_MIN);  
  6. }  
  7.   
  8. // 對比兩個 double 是否相等  
  9. inline  
  10. bool double_equal(double l, double r)  
  11. {  
  12.     return (l >= r ? l - r < DBL_EPSILON : r - l < DBL_EPSILON);  
  13. }  
  14.   
  15. // 打印函數的結果  
  16. #define TEST_BOOL_FUNC(func) _tprintf(_T("%s: %s\n"), _TSTRINGIZE(func), func ? _T("TRUE") : _T("FALSE"))  
  17.   
  18. // 用戶代碼  
  19. // 對比 double 是否為 0 時,double_equal0() 更精確  
  20. // 對比兩個 double 是否相等時,最好用 double_equal()  
  21.   
  22. TEST_BOOL_FUNC(double_equal0(0));                       // TRUE  
  23. TEST_BOOL_FUNC(double_equal0(DBL_EPSILON));             // FALSE  
  24. TEST_BOOL_FUNC(double_equal0(-DBL_EPSILON));            // FALSE  
  25. TEST_BOOL_FUNC(double_equal0(DBL_MIN));                 // FALSE  
  26. TEST_BOOL_FUNC(double_equal0(-DBL_MIN));                // FALSE  
  27.   
  28. TEST_BOOL_FUNC(double_equal(0, 0));                     // TRUE  
  29. TEST_BOOL_FUNC(double_equal(DBL_EPSILON, 0));           // FALSE  
  30. TEST_BOOL_FUNC(double_equal(DBL_MIN, 0));               // TRUE  
  31. TEST_BOOL_FUNC(double_equal(1.0, 1.0 + DBL_EPSILON));   // FALSE  
  32. TEST_BOOL_FUNC(double_equal(1.0, 1.0 - DBL_EPSILON));   // FALSE  
  33. TEST_BOOL_FUNC(double_equal(1.0, 1.0 + DBL_MIN));       // TRUE  
  34. TEST_BOOL_FUNC(double_equal(1.0, 1.0 - DBL_MIN));       // TRUE  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">math.h 數學常量^

數學計算常用的浮點數常量,如 M_PI (pi), M_E (e), M_SQRT2 (sqrt(2)) 等。這些數學常量不是標准 C/C++ 的一部分,而是 Microsoft 的擴展,使用前需要定義 _USE_MATH_DEFINES:

  1. #define _USE_MATH_DEFINES  
  2. #include <math.h>  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">EOF 常量^

EOF (end-of-file) 常量,定義為 (-1),有寬字符版 WEOF ((wint_t)(0xFFFF)),EOF 和 WEOF 在 stdio.h 中定義,還有 _TCHAR 版 _TEOF,在 tchar.h 中定義。EOF 在流、I/O 操作中表示到達流、文件末尾(EOF 條件),也用來表示發生錯誤情況

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:標准輸入的 EOF^

  1. // 設置 locale  
  2. // 定義寬字符流與控制台 I/O 字符之間的轉換字符集編碼為系統 ANSI 字符集  
  3. // 這樣在中文 Windows 上可輸入、顯示中文字符  
  4. _tsetlocale(LC_ALL, _T(""));  
  5.   
  6. // 要用存儲空間 >= _gettchar() 返回值類型的變量保存其返回值  
  7. // 而不要用 char ch = _getchar(),那樣會截斷其返回值類型  
  8. int ch;  
  9. while ((ch = _gettchar()) != _TEOF)  
  10.     _tprintf(_T("[%c]"), (_TCHAR)ch);  
  11.   
  12. _tprintf(_T("\nread stdin: %s\n"), (feof(stdin) ? _T("EOF") : _T("Error")));  

測試輸出,用 Ctrl + Z 產生 EOF 信號:

  1. abc漢字  
  2. [a][b][/c][漢][字][  
  3. ]^Z  
  4.   
  5. read stdin: EOF  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">errno.h 錯誤代碼^

在 errno.h 中定義,是測試全局量 errno 的值,errno 在 VC 中實現為線程安全的函數,而非全局變量。錯誤代碼以 E 打頭如 EINVAL:不合法的參數錯誤

錯誤代碼具體值參考 MSDN: errno Constants 和 errno, _doserrno, _sys_errlist, and _sys_nerr

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">locale 類別^

locale 類別 (Categories),在 locale.h 中定義,如 LC_ALL、LC_CTYPE

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">_MAX_PATH 等文件名與路徑長度限制^

包括全路徑與各部分路徑的限制,即 FILENAME_MAX、_MAX_PATH、_MAX_DRIVE、_MAX_EXT、_MAX_FNAME、_MAX_DIR,在 stdlib.h 中定義。最大全路徑長度限制在 260,和 Windows 的 MAX_PATH 相同,這是為了兼容 Windows 98 FAT32 文件系統。CRT 支持 32767 長度的文件名,方法和 Windows API 相同,即使用 "\\?\" 路徑前綴,並調用 Unicode 寬字符版的 CRT 函數

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">RAND_MAX 隨機數最大值^

在 stdlib.h 中定義為 32767,rand() 函數會產生 0 ~ RAND_MAX 之間的偽隨機 int 值

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:用 RAND_MAX 產生某個范圍內的隨機數^

  1. template<bool seed, typename Type>  
  2. inline  
  3. Type get_rand(Type min, Type max)  
  4. {  
  5.     _ASSERT(max >= min);  
  6.   
  7.     if (seed)       // Release 方式編譯時,這個判斷語句會被優化掉  
  8.         srand((unsigned int) time(NULL));  
  9.   
  10.     return (Type) (((double) rand() / (double) RAND_MAX) * (max - min) + min);  
  11. }  
  12.   
  13. template<typename Type>  
  14. inline  
  15. Type get_rand_seed(Type min, Type max)  
  16. {  
  17.     return get_rand<true>(min, max);  
  18. }  
  19.   
  20. template<typename Type>  
  21. inline  
  22. Type get_rand_noseed(Type min, Type max)  
  23. {  
  24.     return get_rand<false>(min, max);  
  25. }  
  26.   
  27. // 用戶代碼  
  28. #define RANGE_MIN   10  
  29. #define RANGE_MAX   100  
  30.   
  31. int randnum;  
  32. randnum = get_rand_seed(RANGE_MIN, RANGE_MAX);  
  33. randnum = get_rand_noseed(RANGE_MIN, RANGE_MAX);  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">va_arg/va_start/va_end 訪問變長函數參數^

用於訪問類似 printf(const char* format, ...) 等變長函數參數的輔助宏,在 stdarg.h 中聲明,參考 MSDN: va_arg, va_end, va_start

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">宏實現的 CRT 函數^

在 VC CRT 中有些函數以宏和函數兩種方式實現,如 getchar(),並優先使用宏版本,

強制使用函數版的方法:

(1). 調用時給函數名加括號,如 (getchar)()
(2). 調用前,取消宏版本的定義,如 #undef getchar

兩種實現方式的比較見 MSDN: Recommendations for Choosing Between Functions and Macros

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">Microsoft 預定義宏^

VC C/C++ 和 Microsoft 預定義宏參考 MSDN: Predefined Macros

這些宏可以分類如下:

平台與系統類^

_M_IX86: IA32/x86 平台
_M_IA64: IA64/IPF (Itanium Processor Family) 64bit 平台
_M_X64: x64/x86-64/AMD64 平台
WIN32_WIN32: Win32 和 Win64 程序開發都會定義
_WIN64: Win64 程序開發
_CONSOLE: 控制台 Windows 程序開發,鏈接 Console 子系統:/SUBSYSTEM:CONSOLE
_WINDOWS: 非控制台 Windows 程序開發,鏈接 Windows 子系統:/SUBSYSTEM:WINDOWS

版本號類^

通常定義為數字,配合 #if (XXX >= 1000) 使用,啟動、禁用特定部分的代碼、特性

_MSC_VER: VC 編譯器 cl 版本號。VC 2003 編譯器版本號 13.10 (_MSC_VER = 1310),VC 2005 編譯器版本號 14.00 (_MSC_VER = 1400)。用 cl /? 查看編譯器版本號
_MFC_VER: MFC 版本號
_ATL_VER: ATL 版本號
__CLR_VER: CLR 版本號
WINVER: 目標 Windows 版本號
_WIN32_WINNT: 目標 Windows NT 版本號
_WIN32_WINDOWS: 目標 Windows 9x 版本號
_WIN32_IE: 目標 IE 版本號

工程配置管理類^

_DEBUGNDEBUG: Debug/Release 編譯方式
UNICODE_UNICODE_MBCS: ANSI/UNICODE/MBCS 字符集支持
_AFXDLL: 動態鏈接 MFC (DLL)
_ATL_STATIC_REGISTRY_ATL_DLL: 靜態/動態鏈接 ATL
_DLL: 動態鏈接 CRT (DLL),對應 /MD、/MDd 編譯選項
_MT: CRT 多線程支持,目前 4 種 CRT 鏈接方式 /MD、/MDd、/MT、/MTd 都支持多線程(VC 2005 已沒有單線程版 CRT),加上創建 DLL 模塊的 /LD、/LDd,都定義 _MT
_MANAGED: 以 /clr、/clr:pure、/clr:safe 托管方式編譯時,定義為 1
__cplusplus_cli: 以 /clr、/clr:pure、/clr:safe 方式編譯時定義,VC 2005 中定義為 200406L

上面 1、2、3 類宏通常和條件編譯預處理指令 #if/#ifdef/#ifndef 配合使用

輔助類^

__VA_ARGS__: 在函數式宏中,代表變長部分參數 (...),參考 MSDN: Variadic Macros

__COUNTER__: include 展開編譯單元后,編譯時第一次遇到 __COUNTER__ 替換為 0,以后在這個編譯每遇到一次 __COUNTER__ 自增一。不同的編譯單元之間 __COUNTER__ 不互相積累疊加,均從 0 開始計數,但預編譯頭 .pch 文件會記錄 __COUNTER__ 的歷史值,則每個編譯單元均從歷史值 + 1 開始計數。__COUNTER__ 支持宏的嵌套展開

__FUNCTION____FUNCDNAME____FUNCSIG__: 表示所在函數的函數名的 char 字符串。例如,對於 void test_funcname_macro() 函數原型,它們的值如下:

(1). __FUNCTION__ = test_funcname_macro: 函數的原始名/非修飾名 (undecorated)
(2). __FUNCDNAME__ = ?test_funcname_macro@@YAXXZ: 函數的修飾名 (decorated),可用工具 undname "decorated_name" 得出函數原型和調用規范,即 __FUNCSIG__ 所表示的
(3). __FUNCSIG__ = void __cdecl test_funcname_macro(void): 函數的 signature 名,即調用約定、返回值類型、參數類型

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:用 __VA_ARGS__ 打印跟蹤函數調用^

這個 CALL_TRACE 功能不實用,只為說明 __VA_ARGS__ 用法:

  1. // 針對參數不為 void,且需要保存返回值的函數  
  2. #define CALL_TRACE(func, ret, ...)      { _tprintf(_T("call: %s\n"), _TSTRINGIZE(func)); ret = func(__VA_ARGS__); }  
  3. // 針對返回值為 void 或不關心返回值的函數  
  4. #define CALL_TRACE_VOID(func, ...)      { _tprintf(_T("call: %s\n"), _TSTRINGIZE(func)); func(__VA_ARGS__); }  
  5.   
  6. // 針對參數為 void 的函數  
  7. // NOTE: 函數 func() 使用 func(__VA_ARGS__) 展開時,會影響前面的變長參數函數 _tprintf(),  
  8. // 導致運行時緩沖區訪問違例(Debug 方式產生保護中斷),所以不能用前兩版帶 func(__VA_ARGS__) 的 CALL_TRACE  
  9. #define CALL_TRACE_VOIDPARM(func, ret)  { _tprintf(_T("call: %s\n"), _TSTRINGIZE(func)); ret = func(); }  
  10.   
  11. // 針對返回值、參數均為 void 的函數  
  12. #define CALL_TRACE_VOID_VOIDPARM(func)  { _tprintf(_T("call: %s\n"), _TSTRINGIZE(func)); func(); }  
  13.   
  14. // 用戶代碼  
  15. // Unicode 方式編譯時,輸出 call: CreateFileW,並將返回值傳給 hFile  
  16. CALL_TRACE_RET(CreateFile, hFile, _T("bbb"), 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:用 __VA_ARGS__ 格式化 std::string^

  1. namespace std  
  2. {  
  3. typedef std::basic_string<_TCHAR>   _tstring;  
  4. }  
  5.   
  6. #define FORMAT_STRING(str, buf, sz, ...)    { sprintf_s(buf, sz, __VA_ARGS__); str = buf; }  
  7. #define FORMAT_WSTRING(str, buf, sz, ...)   { swprintf_s(buf, sz, __VA_ARGS__); str = buf; }  
  8. #define FORMAT_TSTRING(str, buf, sz, ...)   { _stprintf_s(buf, sz, __VA_ARGS__); str = buf; }  
  9.   
  10. // 用戶代碼  
  11. _TCHAR buf[512];  
  12. _tstring str;  
  13. FORMAT_TSTRING(str, buf, _countof(buf), _T("%s is: %f"), _T("Pi"), M_PI);  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:用 __COUNTER__ 計數值定義掩碼常量^

這種方法限制很多,並不實用,如 MyMask 之后再定義另一個掩碼列舉型時,會從 __COUNTER__ 的歷史值而非 0 開始:

  1. // 保證 MAKE_MASK 在所有其它使用 __COUNTER__ 代碼之前,這樣才能  
  2. // 保證第一次 MAKE_MASK 時,產生 2 << 0  
  3. #define MAKE_MASK0(maskname)    maskname = 1  
  4. #define MAKE_MASK(maskname)     maskname = (2 << __COUNTER__)   // 說明 __COUNTER__ 是支持嵌套展開的  
  5.   
  6. // 用戶代碼  
  7. enum MyMask  
  8. {  
  9.     MAKE_MASK0(MASK_0), //  2^0:    1  
  10.     MAKE_MASK(MASK_1),  //  2^1:    2 << 0  
  11.     MAKE_MASK(MASK_2),  //  2^2:    2 << 1  
  12.     MAKE_MASK(MASK_3),  //  2^3:    2 << 2  
  13.     MAKE_MASK(MASK_4)   //  2^4:    2 << 3  
  14.     // 最大 MASK = MASK_31  2^31:   2 << 30  
  15. };  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:用 __FUNCTION__ 打印跟蹤函數調用^

  1. #define BEGIN_FUNC  _tprintf(_T("%s BEGIN\n"), _T(__FUNCTION__));  
  2. #define END_FUNC    _tprintf(_T("%s END\n"), _T(__FUNCTION__));  
  3.   
  4. // 用戶代碼  
  5. void test_funcname_macro()  
  6. {  
  7.     BEGIN_FUNC  
  8.     // 函數的功能代碼  
  9.     END_FUNC  
  10. }  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">Windows API 中的注釋性宏^

注釋性宏,即是否使用它們不影響編譯結果,通常定義為空

目的:

(1). 在源代碼中起到注解 (annotation) 和標注 (marker) 作用,便於閱讀和理解代碼功能
(2). 指導 lint 等靜態代碼檢查工具檢查代碼缺陷
(3). 指導文檔自動生成工具掃描源文件,生成類、函數/API 參考文檔

如 WinDef.h 中定義的 IN、OUT、OPTIONAL 用來說明函數參數或類型成員的傳入、傳出、可選性質

sal.h 中有更完整和復雜的注釋性宏,SAL (Source code Annotation Language) 參考 sal.h 源文件和 MSDN: SAL Annotations

Windows API 和 CRT 都用 SAL 注釋,幾個常用的如下:

__in: 傳入參數
__out: 傳出參數
__inout: 傳入且傳出參數
__in_opt, __out_opt, __inout_opt: 可選參數,可以為 NULL

如 CreateFileW() 的聲明:

  1. // WinBase.h  
  2.   
  3. WINBASEAPI  
  4. __out  
  5. HANDLE  
  6. WINAPI  
  7. CreateFileW(  
  8.     __in     LPCWSTR lpFileName,  
  9.     __in     DWORD dwDesiredAccess,  
  10.     __in     DWORD dwShareMode,  
  11.     __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,  
  12.     __in     DWORD dwCreationDisposition,  
  13.     __in     DWORD dwFlagsAndAttributes,  
  14.     __in_opt HANDLE hTemplateFile  
  15.     );  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">Windows API 中的常用宏^

Windows API 包含大量旗標、掩碼、狀態碼、錯誤碼等常量式宏

函數式宏最常用的有:

類型輔助類^

  1. BYTE    HIBYTE(WORD wValue)  
  2. BYTE    LOBYTE(WORD wValue)  
  3.   
  4. WORD    HIWORD(DWORD dwValue)  
  5. WORD    LOWORD(DWORD dwValue)  
  6. WORD    MAKEWORD(BYTE bLow, BYTE bHigh)  
  7.   
  8. LONG    MAKELONG(WORD wLow, WORD wHigh)  
  9. LRESULT MAKELRESULT(WORD wLow, WORD wHigh)  
  10. LPARAM  MAKELPARAM(WORD wLow, WORD wHigh)  
  11. WPARAM  MAKEWPARAM(WORD wLow, WORD wHigh)  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">GDI 類^

  1. DWORD       MAKEROP4(DWORD fore, DWORD back): used in MaskBlt()  
  2.   
  3. LONG        DIBINDEX(WORD wColorTableIndex)  
  4. COLORREF    PALETTEINDEX(WORD wPaletteIndex)  
  5.   
  6. COLORREF    PALETTERGB(BYTE bRed, BYTE bGreen, BYTE bBlue)  
  7. COLORREF    RGB(BYTE byRed, BYTE byGreen, BYTE byBlue)  
  8.   
  9. BYTE        GetBValue(DWORD rgb)  
  10. BYTE        GetGValue(DWORD rgb)  
  11. BYTE        GetRValue(DWORD rgb)  
  12.   
  13. POINTS      MAKEPOINTS(DWORD dwValue)  

另外,BITMAP_WIDTHBYTES(bits) 不在 Windows API 中,但比較常用於位圖:

  1. // 輸入:位圖圖像中一行的邏輯位數 = 位圖像素寬 x 每像素位數  
  2. // 輸出:位圖圖像中一行占用的字節數,按 4 Bytes 對齊  
  3. #define BITMAP_WIDTHBYTES(bits)     (((bits) + 31) >> 5 << 2)  

錯誤處理類^

標記沒有使用的參數、變量輔助宏^

UNREFERENCED_PARAMETER(P)
DBG_UNREFERENCED_PARAMETER(P)
DBG_UNREFERENCED_LOCAL_VARIABLE(V)

讓沒有使用的參數、變量不產生編譯警告,並且關閉 lint 缺陷檢查報告

錯誤碼、狀態碼^

Windows 有三大錯誤碼、狀態碼空間:

(1). Win32 狀態碼:GetLastError() 所返回,DWORD 類型,WinError.h 中定義
(2). COM 狀態碼:COM 函數用,HRESULT 類型,WinError.h 中定義
(3). 內核狀態碼:內核函數和低級 API 用,NTSTATUS 類型,ntstatus.h 中定義

狀態碼有關的宏:

  1. MAKE_HRESULT(sev, fac, code): 將 severity、facility、code 合並為 HRESULT  
  2. HRESULT_CODE(hr): 取得 HRESULT 的 code 部分  
  3. HRESULT_FACILITY(hr): 取得 HRESULT 的 facility 部分  
  4. HRESULT_SEVERITY(hr): 取得 HRESULT 的 severity 位  
  5.   
  6. HRESULT_FROM_NT(nt_stat): 從 NTSTATUS 變換到 HRESULT  
  7. HRESULT_FROM_WIN32(win_err): 從 Win32 狀態碼變換到 HRESULT  
  8.   
  9. SUCCEEDED(hr): HRESULT 是否表示成功  
  10. FAILED(hr): HRESULT 是否表示失敗  
  11. IS_ERROR(hr): HRESULT 是否表示一個錯誤  

Win32 狀態碼沒有類似 MAKE_HRESULT 的宏,自定義 Win32 狀態碼時可以用 mc (Message Compiler) 工具處理 .mc 腳本,自動生成含自定義 Win32 狀態碼的頭文件,同時生成用於 FormatMessage() 的狀態碼文本描述,參考 MSDN:Message Compiler

也可以自定義用於 Win32 狀態碼的 MAKE_WINERR():

  1. //  copy from WinError.h  
  2. //  
  3. //  Values are 32 bit values layed out as follows:  
  4. //  
  5. //   3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1  
  6. //   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0  
  7. //  +---+-+-+-----------------------+-------------------------------+  
  8. //  |Sev|C|R|     Facility          |               Code            |  
  9. //  +---+-+-+-----------------------+-------------------------------+  
  10. //  
  11. //  where  
  12. //  
  13. //      Sev - is the severity code  
  14. //  
  15. //          00 - Success  
  16. //          01 - Informational  
  17. //          10 - Warning  
  18. //          11 - Error  
  19. //  
  20. //      C - is the Customer code flag  
  21. //  
  22. //      R - is a reserved bit  
  23. //  
  24. //      Facility - is the facility code  
  25. //  
  26. //      Code - is the facility's status code  
  27. //  
  28.   
  29. // Win32 狀態碼的各部分起始位、位掩碼和位長度  
  30.   
  31. #define WINERR_SEVERITY_BIT_LOW         30  
  32. #define WINERR_SEVERITY_MASK            0xC0000000  
  33. #define WINERR_SEVERITY_BIT_LEN         2  
  34. #define WINERR_SEVERITY_VALUE(val)      (((val) << WINERR_SEVERITY_BIT_LOW) & WINERR_SEVERITY_MASK)  
  35.   
  36. #define WINERR_CUSTOM_DEFINE_BIT_LOW    29  
  37. #define WINERR_CUSTOM_DEFINE_MASK       0x20000000  
  38. #define WINERR_CUSTOM_DEFINE_BIT_LEN    1  
  39. #define WINERR_CUSTOM_DEFINE_FLAG       (1 << WINERR_CUSTOM_DEFINE_BIT_LOW)  
  40.   
  41. #define WINERR_FACILITY_BIT_LOW         16  
  42. #define WINERR_FACILITY_MASK            0x0FFF0000  
  43. #define WINERR_FACILITY_BIT_LEN         12  
  44. #define WINERR_FACILITY_VALUE(val)      (((val) << WINERR_FACILITY_BIT_LOW) & WINERR_FACILITY_MASK)  
  45.   
  46. #define WINERR_CODE_BIT_LOW             0  
  47. #define WINERR_CODE_MASK                0x0000FFFF  
  48. #define WINERR_CODE_BIT_LEN             16  
  49. #define WINERR_CODE_VALUE(val)          (val) & WINERR_CODE_MASK  
  50.   
  51. // Win32 狀態碼中的嚴重級別 severity  
  52.   
  53. #define WINERR_SEVERITY_SUCCESS         0  
  54. #define WINERR_SEVERITY_INFORM          1  
  55. #define WINERR_SEVERITY_WARNING         2  
  56. #define WINERR_SEVERITY_ERROR           3  
  57. #define WINERR_SEVERITY_NOT_CARE        3  
  58.   
  59. // 自定義 Win32 狀態碼的宏  
  60. #define MAKE_WINERR(sev, fac, code)     \  
  61.     ((DWORD)(WINERR_SEVERITY_VALUE(sev) | WINERR_CUSTOM_DEFINE_FLAG | WINERR_FACILITY_VALUE(fac) | WINERR_CODE_VALUE(code)))  

調用規范類^

調用規范/約定參考 MSDN: Calling Conventions

Windows API 使用的調用規范名稱宏,在 WinDef.h 中定義:

  1. #define CALLBACK    __stdcall  
  2. #define WINAPI      __stdcall  
  3. #define WINAPIV     __cdecl  
  4. #define APIENTRY    WINAPI  
  5. #define APIPRIVATE  __stdcall  
  6. #define PASCAL      __stdcall  

COM 常用的調用規范輔助宏:

  1. EXTERN_C: C 鏈接約定  
  2.   
  3. STDAPI: __stdcall,C 鏈接約定,返回 HRESULT  
  4. STDAPI_(type): __stdcall,C 鏈接約定,返回 type 類型  
  5.   
  6. STDMETHOD(method): __stdcall,返回 HRESULT 的類成員虛函數  
  7. STDMETHOD_(type, method): __stdcall,返回 type 類型的類成員虛函數  
  8. STDMETHODIMP: __stdcall,返回 HRESULT,對應 STDMETHOD(method) 實現  
  9. STDMETHODIMP_(type): __stdcall,返回 type 類型,對應 STDMETHOD_(type, method) 實現  

國際化類^

  1. WORD    LANGIDFROMLCID(LCID lcid)  
  2. WORD    MAKELANGID(USHORT primaryLang, USHORT subLang)  
  3. DWORD   MAKELCID(WORD langID, WORD sortID)  
  4. DWORD   MAKESORTLCID(WORD langID, WORD sortID, WORD sortVersion)  
  5. WORD    PRIMARYLANGID(WORD lgid)  
  6. WORD    SORTIDFROMLCID(LCID lcid)  
  7. WORD    SORTVERSIONFROMLCID(LCID lcid)  
  8. WORD    SUBLANGID(WORD lgid)  

資源類^

  1. LPTSTR  MAKEINTRESOURCE(WORD wInt)  
  2. BOOL    IS_INTRESOURCE(WORD wInt)  

網絡類^

  1. LPARAM  MAKEIPADDRESS(BYTE b0, BYTE b1, BYTE b2, BYTE b3)  
  2. BYTE    FIRST_IPADDRESS(LPARAM lParam)  
  3. BYTE    SECOND_IPADDRESS(LPARAM lParam)  
  4. BYTE    THIRD_IPADDRESS(LPARAM lParam)  
  5. BYTE    FOURTH_IPADDRESS(LPARAM lParam)  
  6. LPARAM  MAKEIPRANGE(BYTE low, BYTE high)  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">字符串化操作符 #^

將代碼中某個名字轉換為字符串字面量,即“加引號”,參考 MSDN: Stringizing Operator

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">用 # 操作構造字符串化宏 STRINGIZE^

  1. #define __STRINGIZE(x)  # x  
  2. #define _STRINGIZE(x)   __STRINGIZE(x)  
  3. #define _TSTRINGIZE(x)  _T(_STRINGIZE(x))  

說明:

(1). # x 產生的是 char 字符串,非 wchar_t 字符串,需配合 _T() 使用

(2). _MACRO() 再次調用 __MACRO() 是一種針對 # 和 ## 操作的常用編寫技巧。因為 #、## 操作比較特殊,當它處於宏體中時,不會進行嵌套展開,如 __TSTRINGIZE(NULL) 展開為 "NULL" 而非 "0",要想嵌套展開,再定義一層 _STRINGIZE() 調用 __STRINGIZE() 即可,_TSTRINGIZE(NULL) 展開為 "0"

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">CRT 中的 STRINGIZE 定義^

CRT 中有類似上面的 STRINGIZE(),以及寬字符化字面量宏 _CRT_WIDE() 的定義:

  1. // crtdefs.h  
  2. #ifndef _CRT_STRINGIZE  
  3. #define __CRT_STRINGIZE(_Value) #_Value  
  4. #define _CRT_STRINGIZE(_Value) __CRT_STRINGIZE(_Value)  
  5. #endif  
  6.   
  7. #ifndef _CRT_WIDE  
  8. #define __CRT_WIDE(_String) L ## _String  
  9. #define _CRT_WIDE(_String) __CRT_WIDE(_String)  
  10. #endif  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">STRINGIZE 的展開規則^

1. 如果 _STRINGIZE() 的參數是宏,那么宏代表的實際值也將被展開,即嵌套展開

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:用 STRINGIZE 查看宏的展開結果^

查看某個宏在當前編譯配置 (Debug/Release, ANSI/Unicode) 下,實際表示的東西,如某個 _t 系列函數、Windows API 究竟表示哪個函數,可以利用 _STRINGIZE():

  1. // 將輸出實際的行號、數字,而非字符串 "__LINE__"、"MAX_PATH"  
  2. _tprintf(_T("Line: %s\n"), _TSTRINGIZE(__LINE__));  
  3. _tprintf(_T("MAX_PATH: %s\n"), _TSTRINGIZE(MAX_PATH));  
  4.   
  5. // 判斷宏的當前值、調用了哪個版本的 _t 系列函數、Windows API  
  6. _tprintf(_T("_DEBUG: %s, _UNICODE: %s\n"), _TSTRINGIZE(_DEBUG), _TSTRINGIZE(_UNICODE));  
  7. _tprintf(_T("_tprintf: %s\n"), _TSTRINGIZE(_tprintf));  
  8. _tprintf(_T("CreateFile: %s\n"), _TSTRINGIZE(CreateFile));  

輸出結果:

  1. Line: 24  
  2. MAX_PATH: 260  
  3. _DEBUG: 1, _UNICODE: 1  
  4. _tprintf: wprintf  
  5. CreateFile: CreateFileW  

2. 如果 _STRINGIZE() 的參數單純的變量、函數、類型、const、enum 常量,那么只是將 _STRINGIZE() 括號中的東西加引號而已,如下:

  1. // 非 const、其它內部類型 double、char,結果都一樣  
  2. const int val = 260;  
  3.   
  4. // 枚舉常量  
  5. enum MUSIC_STATE  
  6. {  
  7.     ST_STOP,  
  8.     ST_PLAY,  
  9.     ST_PAUSE,  
  10.     ST_BUTT  
  11. };  
  12.   
  13. // 自定義結構、類  
  14. ClassTest obj;  
  15.   
  16. // 函數  
  17. void func(int a);  
  18.   
  19. // 下面輸出 _TSTRINGIZE() 括號中名字加上引號得到的字符串,而非實際變量值  
  20. _tprintf(_T("int: %s, val: %s\n"), _TSTRINGIZE(int), _TSTRINGIZE(val));  
  21. _tprintf(_T("MUSIC_STATE: %s, ST_STOP: %s\n"), _TSTRINGIZE(MUSIC_STATE), _TSTRINGIZE(ST_STOP));  
  22. _tprintf(_T("ClassTest: %s, obj: %s\n"), _TSTRINGIZE(ClassTest), _TSTRINGIZE(obj));  
  23. _tprintf(_T("func: %s\n"), _TSTRINGIZE(func));  

輸出結果:

  1. int: int, val: val  
  2. MUSIC_STATE: MUSIC_STATE, ST_STOP: ST_STOP  
  3. ClassTest: ClassTest, obj: obj  
  4. func: func  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">拼接操作符 ##^

將代碼中兩個名字拼接到一起,形成一個名字。## 操作“不加引號”,參考 MSDN: Token-Pasting Operator

  1. #define __CONCAT(x, y)  x ## y  
  2. #define _CONCAT(x, y)   __CONCAT(x, y)  

## 與 # 一樣對其操作數不進行嵌套展開,所以 __CONCAT(aaa, __CONCAT(bbb, ccc)) 的展開結果是 aaa__CONCAT(bbb, ccc),而 _CONCAT(aaa, _CONCAT(bbb, ccc)) 的展開結果是 aaabbbccc。## 的結果是名字拼接,而不是字符串字面量,即不是 "aaabbbccc"

通常用 ## 操作拼接構造類型、變量、函數的名字

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:_T() 的定義^

  1. // tchar.h  
  2. #ifdef _UNICODE  
  3. #define __T(x)      L ## x  
  4. #else  
  5. #define __T(x)      x  
  6.   
  7. #define _T(x)       __T(x)  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:Windows API 通用句柄類型的定義^

  1. // winnt.h  
  2. typedef void *HANDLE;  
  3. #define DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name  
  4.   
  5. // 因此多數 Windows 句柄是指向樁結構的指針,如 HWND:  
  6.   
  7. // windef.h  
  8. DECLARE_HANDLE  (HWND);  
  9.   
  10. // HWND 定義展開后是:  
  11. struct HWND__  
  12. {  
  13.     int unused;  
  14. };  
  15. typedef struct HWND__ *HWND;  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:用 ## 構造函數名^

  1. // 音樂播放狀態常量  
  2. enum MUSIC_STATE  
  3. {  
  4.     ST_STOP,  
  5.     ST_PLAY,  
  6.     ST_PAUSE,  
  7.     ST_BUTT  
  8. };  
  9.   
  10. // 音樂播放狀態結構  
  11. // 里面有一個用於處理特定狀態的回調函數 stat_proc  
  12. typedef struct _MusicState  
  13. {  
  14.     MUSIC_STATE     stat;  
  15.     const _TCHAR*   stat_name;  
  16.     int             (*stat_proc)(void*);  
  17. } MusicState;  
  18.   
  19. // 處理特定音樂播放狀態的函數  
  20. // 函數名的統一形式 proc_ ## stat,stat 是狀態常量的名字  
  21. int proc_ST_STOP(void*);  
  22. int proc_ST_PLAY(void*);  
  23. int proc_ST_PAUSE(void*);  
  24.   
  25. // 初始化音樂播放狀態結構  
  26. #define INIT_MUSIC_STATE(stat)  {stat, _TSTRINGIZE(stat), proc_ ## stat}  
  27.   
  28. MusicState g_MusicState[ST_BUTT] =  
  29. {  
  30.     INIT_MUSIC_STATE(ST_STOP),  
  31.     INIT_MUSIC_STATE(ST_PLAY),  
  32.     INIT_MUSIC_STATE(ST_PAUSE)  
  33. };  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">TCHAR 統一字符類型和處理^

_TCHAR、_T()、_t 系列函數等東西叫做 Generic-Text Mapping,即使用宏進行統一字符類型編寫,在不同的字符集編碼工程配置 ANSI/UNICODE/MBCS 下替換為不同的實際函數或類型,參考 MSDN:Generic-Text Mappings,Using Generic-Text MappingsUsing TCHAR.H Data Types with _MBCS

工程的字符集配置的宏定義:

ANSI (SBCS, ASCII): _UNICODE 和 _MBCS 均未定義,使用 char 單字節字符集編碼
UNICODE: _UNICODE 定義,使用 wchar_t 寬字符集編碼,VC 默認 wchar_t 2 字節
MBCS: _MBCS 定義,使用 char 變長字符集編碼,一個字符占一個或多個 char

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">_TCHAR, _TEXT()/_T(), _t 系列函數^

根據 _UNICODE、_MBCS 的定義,調用 ANSI/UNICODE/MBCS 不同字符集版本的 CRT 函數,或產生字面量,多在 tchar.h 中聲明。_t 字符操作函數參考 MSDN:String Manipulation (CRT)

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">TCHAR, LPTSTR/LPCTSTR, TEXT(), A/W 版本 Windows API^

根據 UNICODE 的定義,調用 ANSI/UNICODE 不同字符集版本的 Windows API,或產生字面量,多在 WinBase.h、Windows.h 中聲明

不成文約定:帶 _ 前綴的代碼,通常對應 CRT,而不帶 _ 前綴的東西,通常對應 Windows API。A/W 版本 API 都是接收字符串參數的函數,但並非所有接收字符串的 API 都有 A/W 兩個版本,如 GetProcAddress() 只是 A 版本函數,因為 DLL 中的導出符號用 ASCII 英文足夠了

宏的缺點和替代方法^

宏是預編譯行為,所做的是名字替換,它的缺點和替代方法如下:

宏難於調試^

編譯時,宏不會產生用於調試的名字符號。如 #define MAX_PATH 260,在調試時,無法找到其符號名 MAX_PATH,而只是 260 數字

常量式宏可以用 const 和 enum 代替,在調試中可以查看 const、enum 的符號名,並且 const、enum 和宏的運行時開銷是相同的(有使用 const、enum 時才會分配內存):

  1. const char* DEF_FILENAME = "default.txt";  
  2.   
  3. enum BUF_SIZE  
  4. {  
  5.     BUF_SIZE_SMALL      = 64,  
  6.     BUF_SIZE_MEDIUM     = 256,  
  7.     BUF_SIZE_LARGE      = 1024  
  8. };  

另外,在 VC 2005 中,進行 C/C++ 源碼級別調試時,函數式宏無法像 inline 或普通函數一樣使用 Step into 進入宏定義體的代碼,宏調用被視為一條語句,只能使用 Go To Definition (F12) 跳轉到宏定義處查看代碼,而不能調試

宏的使用缺陷^

(1). 宏以字面形式展開,有副作用,典型的有兩種:

(a). 宏參數不加括號展開時改變邏輯,如 #define RECT_AREA(x, y) (x * y)

解決方法:定義宏時給參數的使用加上括號,如 #define RECT_AREA(x, y) ((x) * (y))

(b). 宏體為多行語句,如果放到判斷語句中,並且不加 {} 包起來,只有第一句在判斷語句下執行,其它在判斷語句外,如下例:

  1. #define SWAP(v1, v2, tmp)   \  
  2.     tmp = v1;               \  
  3.     v1 = v2;                \  
  4.     v2 = tmp;  
  5.   
  6. // 用戶代碼  
  7. if (condition)  
  8.     SWAP(a, b, t);  // 邏輯問題  
  9.   
  10. if (condition) {  
  11.     SWAP(a, b, t);  // OK  
  12. }  

解決方法:定義宏時用 {} 或 do {} while(0) 包起來,如下:

  1. #define SWAP(v1, v2, tmp)   \  
  2.     do {                    \  
  3.         tmp = v1;           \  
  4.         v1 = v2;            \  
  5.         v2 = tmp;           \  
  6.     } while (0)  

(2). 宏對參數沒有類型檢查,宏的返回也不具有類型

(3). 函數式宏,不是函數,不能將其宏名作為函數指針,即不能進行函數回調;也不能進行遞歸調用

函數式宏大多能用 inline 函數 + 函數 template 的方式代替,並保持相同的運行時開銷。但因為 inline 函數是一種 盡力而為 (Try My Best) 的編譯器指示(inline 函數不一定 inline 化,inline 化的程度也不同),實際的開銷根據 inline 函數調用復雜程度(是否有遞歸、作為函數指針)、不同編譯器、不同的工程配置(Debug/Release、編譯選項、編譯優化級別),inline 化有所不同

參考 MSDN: Inline Functions versus Macros

宏造成全局名字空間污染^

宏是全局名字空間的,容易造成名字污染、干擾,可用 const、enum、inline 解決。如下:

  1. class TestClass1  
  2. {  
  3. private:  
  4.     int m_Val;  
  5.   
  6. // private 限制對宏 MACRO_DEF_VAL 不起作用  
  7. #define MACRO_DEF_VAL   128  
  8.   
  9. public:  
  10.     static const int CONST_DEF_VAL = 128;  
  11.     enum { ENUM_DEF_VAL = 128 };  
  12. };  
  13.   
  14. class TestClass2  
  15. {  
  16. private:  
  17.     int m_Val;  
  18.   
  19. // 產生 C4005 警告:MACRO_DEF_VAL 被重復定義  
  20. #define MACRO_DEF_VAL   256  
  21.   
  22. public:  
  23.     static const int CONST_DEF_VAL = 256;  
  24.     enum { ENUM_DEF_VAL = 256 };  
  25. };  
  26.   
  27. // 用戶代碼  
  28.   
  29. // 宏 MACRO_DEF_VAL 是全局的,不能寫為 TestClass1::MACRO_DEF_VAL  
  30. _tprintf(_T("TestClass1: %d, %d, %d\n"), MACRO_DEF_VAL, TestClass1::CONST_DEF_VAL, TestClass1::ENUM_DEF_VAL);  
  31. _tprintf(_T("TestClass2: %d, %d, %d\n"), MACRO_DEF_VAL, TestClass2::CONST_DEF_VAL, TestClass2::ENUM_DEF_VAL);  

輸出結果:

后面定義的宏 MACRO_DEF_VAL 的值將前面的覆蓋了:

  1. TestClass1: 256, 128, 128  
  2. TestClass2: 256, 256, 256  

優先使用宏的情況^

不是所有的宏都能用 const、enum、inline 函數代替:

(1). 對於一些不對應單個函數、變量、常量,並且編碼量大、結構重復的整塊代碼,宏是最合適的選擇,如 MFC 的 RTTI 支持和消息映射結構

(2). 如 例子:C/C++ 預定義宏的取值 “說明 (2)”中所示,需要嵌套展開 __FILE__、__LINE__、__FUNCTION__ 等預定義宏的情況,必需用宏,而不能用 inline 函數

條件編譯^

#if/#else/#elif/#ifdef/#ifndef/#if defined/#if !defined

#ifdef XXX 等價於 #if defined (XXX)
#ifndef XXX 等價於 #if !defined (XXX)

參考 MSDN: The #if, #elif, #else, and #endif Directives

例子:注釋大量代碼^

  1. #if 0  
  2. XXXXXXXXX  
  3. #endif  
  4.   
  5. #if FALSE  
  6. XXXXXXXXX  
  7. #endif  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:MFC 中的調試版代碼示例^

見上文“MFC 的調試版 new”

AssertValid() 參考 MSDN: MFC ASSERT_VALID and CObject::AssertValid

  1. // FrameWindow、Doc、View 等類均可覆蓋以下用於調試的診斷函數  
  2. // 然后可以用 ASSERT_VALID() 診斷其對象狀態是否有效  
  3.   
  4. #ifdef _DEBUG  
  5.     virtual void AssertValid() const;  
  6.     virtual void Dump(CDumpContext& dc) const;  
  7. #endif  
  8.   
  9. // Release 版本的 GetDocument() 是 inline 的  
  10. #ifndef _DEBUG  
  11. inline CMyDoc* CMyView::GetDocument() const  
  12. {  
  13.     return reinterpret_cast<CMyDoc*>(m_pDocument);  
  14. }  
  15. #endif  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:DLL 工程導出符號^

工程 DllProj 中導出符號(變量、函數、類)的方法:在工程中定義宏 DLLPROJ_EXPORTS (/D "DLLPROJ_EXPORTS"),並在使用工程中保證沒有定義 DLLPROJ_EXPORTS

DllProj 導出符號的聲明文件 DllProj.h 如下,在使用工程中 #include 該文件:

  1. // DllProj.h  
  2.   
  3. #ifndef _DLLPROJ_H_  
  4. #define _DLLPROJ_H_  
  5.   
  6. #ifdef DLLPROJ_EXPORTS  
  7. #define DLLPROJ_API __declspec(dllexport)  
  8. #else  
  9. #define DLLPROJ_API __declspec(dllimport)  
  10. #endif  
  11.   
  12. #ifdef __cplusplus  
  13. #define EXTERN_C        extern "C"  
  14. #define EXTERN_C_BEGIN  extern "C" {  
  15. #define EXTERN_C_END    }  
  16. #else   // __cplusplus defined  
  17. #define EXTERN_C        extern  
  18. #define EXTERN_C_BEGIN  
  19. #define EXTERN_C_END  
  20. #endif  // __cplusplus NOT defined  
  21.   
  22. // 導出類  
  23. class DLLPROJ_API TestClass  
  24. {  
  25. public:  
  26.     TestClass();  
  27. };  
  28.   
  29. // 導出全局變量,以 C 的鏈接方式(修飾名、調用約定)  
  30. EXTERN_C DLLPROJ_API int g_TestVal;  
  31.   
  32. // 導出函數  
  33. DLLPROJ_API int TestFunc();  
  34.   
  35. #endif  // _DLLPROJ_H_  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:用 #undef 解決 wxWidgets 自定義事件鏈接 BUG^

BUG 參考:wxEvent derived event,Custom Events

BUG 觸發條件:以 DLL 方式使用 wxWidgets Windows 版本,即定義了 WXUSINGDLL,使用 DECLARE_EVENT_TYPE() 定義自定事件

BUG 表現:以 __declspec(dllimport) 修飾事件標識,實際上事件標識應該是一個模塊內變量,而非導入變量,出現鏈接問題。MinGW GCC 4 報鏈接錯誤,VC 2005 報鏈接警告:warning C4273: inconsistent dll linkage。BUG 的具體原因請跟蹤 wxWidgets 源碼 event.h 中的 DECLARE_EVENT_TYPE() 和 dlimpexp.h 中的 WXDLLIMPEXP_CORE 定義

BUG 典型工程:開源下載器 MultiGet svn version 3

BUG 解決方法

方法 1. 不使用舊的 DECLARE_EVENT_TYPE() 而使用 DECLARE_LOCAL_EVENT_TYPE() 定義自定事件
方法 2. 使用 DECLARE_EVENT_TYPE() 前后包含 undefine_WXDLLIMPEXP_CORE.h、redefine_WXDLLIMPEXP_CORE.h 頭文件,以便取消和重定義 WXDLLIMPEXP_CORE

方法 2 中的 undefine_WXDLLIMPEXP_CORE.h、redefine_WXDLLIMPEXP_CORE.h 以及使用方法如下:

  1. // undefine_WXDLLIMPEXP_CORE.h  
  2.   
  3. // 不要用 #pragma once 等包含一次技巧,因為一個源文件中可能有多個  
  4. // BEGIN_DECLARE_EVENT_TYPES 自定義事件塊,這時要多次包含本文件  
  5.   
  6. #ifdef WXDLLIMPEXP_CORE  
  7. #   define REMOVE_WXDLLIMPEXP_CORE  
  8. #   undef WXDLLIMPEXP_CORE      // 先取消 WXDLLIMPEXP_CORE 定義  
  9. #   define WXDLLIMPEXP_CORE     // 再將其定義為空  
  10. #endif  
  1. // redefine_WXDLLIMPEXP_CORE.h  
  2.   
  3. // 不要用 #pragma once 等包含一次技巧  
  4.   
  5. #ifdef REMOVE_WXDLLIMPEXP_CORE  
  6. #   undef WXDLLIMPEXP_CORE  
  7. // 以下塊拷貝自 wx-2.8.10 dlimpexp.h,用於恢復 WXDLLIMPEXP_CORE 的原有定義  
  8. // BEGIN  
  9. #   ifdef WXMAKINGDLL_CORE  
  10. #       define WXDLLIMPEXP_CORE WXEXPORT  
  11. #       define WXDLLIMPEXP_DATA_CORE(type) WXEXPORT type  
  12. #   elif defined(WXUSINGDLL)  
  13. #       define WXDLLIMPEXP_CORE WXIMPORT  
  14. #       define WXDLLIMPEXP_DATA_CORE(type) WXIMPORT type  
  15. #   else /* not making nor using DLL */  
  16. #       define WXDLLIMPEXP_CORE  
  17. #       define WXDLLIMPEXP_DATA_CORE(type) type  
  18. #   endif  
  19. // END  
  20. #   undef REMOVE_WXDLLIMPEXP_CORE  
  21. #endif  
  1. // 用戶代碼  
  2.   
  3. // 定義 WXDLLIMPEXP_CORE 為空  
  4. #include <undefine_WXDLLIMPEXP_CORE.h>  
  5.   
  6. // 自定義事件  
  7. BEGIN_DECLARE_EVENT_TYPES()  
  8.     DECLARE_EVENT_TYPE(wxEVENT_TEST_TRIGGERED, wxID_ANY)  
  9.     // DECLARE_LOCAL_EVENT_TYPE(wxEVENT_TEST_TRIGGERED, wxID_ANY)   // 用這個不會有 BUG  
  10. END_DECLARE_EVENT_TYPES()  
  11.   
  12. // 重定義 WXDLLIMPEXP_CORE  
  13. #include <redefine_WXDLLIMPEXP_CORE.h>  

說明

(1). #undef 是 #define 的反操作,取消宏定義,而不是將宏定義為空。取消后的宏名可以再次定義,而不產生重定義問題
(2). 大量嵌套的 #if 條件編譯結構,可使用這種預處理縮進方法

預編譯頭文件^

預編譯頭文件 PCH (Precompiled Header) 是對某個編譯單元的編譯結果 (.pch),通常這個編譯單元命名為 [stdafx.cpp include => stdafx.h](VC 工程標准)或 [common.cpp include => common.h]。與常規的編譯結果 (.obj) 不同的是,如果 .pch 的編譯單元源碼在兩次工程編譯期間不改變,則重新編譯工程時,不會重新編譯 .pch

PCH 的特點使它的源碼如 stdafx.h,適合放入很少更改的代碼,如標准庫、運行時庫、系統 API、第三方庫的頭文件,以及工程全局的設置和名字符號,在重新編譯工程時,這些代碼便不會重新編譯,以加快編譯速度

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">使用 PCH 的編譯命令^

以 VC 工程標准的 [stdafx.cpp include => stdafx.h] 預編譯頭文件編譯單元為例,產生、使用 PCH 的編譯命令選項如下:

  • 對除了 stdafx.cpp 之外的其它編譯單元 .c/.cpp(沒有 .h/.hpp,.h/.hpp 是通過 #include 展開到 .c/.cpp 形成編譯單元的),使用如下編譯選項(Debug 工程配置),如果使用 VC IDE 則在工程屬性頁中設置:

    /Yu"stdafx.h" /Fp"Debug\ProjName.pch" 

    /Yu 表示通過 stdafx.h 使用 PCH,/Fp 指定使用的 PCH 路徑為 Debug\ProjName.pch

  • 對 stdafx.cpp,如果使用 VC IDE 則在 stdafx.cpp 的屬性頁中設置,使用如下編譯選項:

    /Yc"stdafx.h" /Fp"Debug\ProjName.pch" 

    /Yc 表示通過 stdafx.h 產生 PCH,/Fp 指定產生的 PCH 路徑為 Debug\ProjName.pch

PCH 的詳細方法參考 MSDN: Creating Precompiled Header Files

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:典型的 MFC 工程預編譯頭 stdafx.h 代碼^

  1. ////////////////////////////////////////////////////////////////////////////////  
  2. ///  
  3. /// @file       stdafx.h  
  4. /// @brief      Windows 標准預編譯頭文件  
  5. ///  
  6. /// 將標准庫、運行時庫、基本庫、Windows API、第三方庫的頭文件在這里包含,生成  
  7. /// 預編譯頭文件 MFCBasic.pch  
  8. ///  
  9. /// 如果不修改 stdafx.h,增量編譯時便不會重新編譯 stdafx.h 中包含的頭文件,這樣  
  10. /// 加快了編譯速度  
  11. ///  
  12. /// @version    <version>  
  13. /// @author     <author>  
  14. /// @date       2011-07  
  15. ///  
  16. /// Copyright (c) 2011, <company>  
  17. /// All rights reserved.  
  18. ///  
  19. ////////////////////////////////////////////////////////////////////////////////  
  20.   
  21. // 典型的“只包含一次”條件編譯技巧  
  22. // VC cl 編譯器版本 10 以上 (_MSC_VER > 1000) 也可以使用 #pragma once 指令  
  23. #ifndef _STDAFX_H_  
  24. #define _STDAFX_H_  
  25.   
  26. // 排除很少使用的 Windows 頭文件  
  27.   
  28. #define WIN32_LEAN_AND_MEAN     // 適用於 Windows API  
  29.   
  30. #ifndef VC_EXTRALEAN  
  31. #define VC_EXTRALEAN            // 適用於 MFC  
  32. #endif  
  33.   
  34. // 指定目標系統和環境 (Windows, IE) 的版本號  
  35.   
  36. #ifndef WINVER  
  37. #define WINVER          0x0501  // 目標系統具有 Windows XP 及以上特性  
  38. #endif  
  39.   
  40. #ifndef _WIN32_WINNT  
  41. #define _WIN32_WINNT    0x0501  // 目標系統具有 Windows XP 及以上特性  
  42. #endif  
  43.   
  44. #ifndef _WIN32_WINDOWS  
  45. #define _WIN32_WINDOWS  0x0410  // 目標系統具有 Windows 98 及以上特性  
  46. #endif  
  47.   
  48. #ifndef _WIN32_IE  
  49. #define _WIN32_IE       0x0600  // 目標系統具有 IE 6.0 及以上特性  
  50. #endif  
  51.   
  52. ////////////////////////////////////////////////////////////////////////////////  
  53. /// Include Header  
  54. ////////////////////////////////////////////////////////////////////////////////  
  55.   
  56. // C 標准庫與運行時庫 (CRT)  
  57. // BEGIN  
  58. //  
  59. #define _CRT_SECURE_NO_DEPRECATE    // 使用廢棄 (deprecated) 的 CRT 函數時,不產生編譯警告  
  60. #define _CRT_SECURE_NO_WARNINGS     // 典型的廢棄函數有不帶緩沖區大小檢查的 strcpy()、strcat()、sprintf() 等  
  61.   
  62. #include <stdlib.h>  
  63. #include <tchar.h>  
  64. #include <crtdbg.h>  
  65. #include <string.h>  
  66. //  
  67. // END  
  68.   
  69. // C++ 標准庫  
  70. // BEGIN  
  71. //  
  72. #include <exception>  
  73. #include <typeinfo>  
  74. //  
  75. // END  
  76.   
  77. // MFC 庫  
  78. // BEGIN  
  79. //  
  80. #ifndef _SECURE_ATL  
  81. #define _SECURE_ATL 1                       // ATL/MFC 的安全設置  
  82. #endif  
  83.   
  84. #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS  // 使 ATL/MFC 的 CString 具有顯式地構造函數 (explicit)  
  85.   
  86. #define _AFX_ALL_WARNINGS                   // 打開 MFC 的所有警告,包括一般可以安全忽略的警告  
  87.   
  88. #include <afxwin.h>     // MFC 核心和標准支持  
  89. #include <afxext.h>     // MFC 擴展支持  
  90.   
  91. #ifndef _AFX_NO_OLE_SUPPORT  
  92. #include <afxdtctl.h>   // MFC 的 IE 4 通用控件支持  
  93. #endif  
  94.   
  95. #ifndef _AFX_NO_AFXCMN_SUPPORT  
  96. #include <afxcmn.h>     // MFC 的 Windows 通用控件支持  
  97. #endif  
  98. //  
  99. // END  
  100.   
  101. // Windows API  
  102. // BEGIN  
  103. //  
  104. // #include <Windows.h>     // 使用 MFC 庫時不要包含 Windows.h,MFC 頭文件中已包含  
  105. #include <Winsock2.h>  
  106. //  
  107. // END  
  108.   
  109. // Windows 通用控件 ComCtl32.dll 版本 6.0 的內嵌 manifest  
  110. // BEGIN  
  111. //  
  112. #ifdef _UNICODE  
  113. #if defined _M_IX86  
  114. #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")  
  115. #elif defined _M_IA64  
  116. #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"")  
  117. #elif defined _M_X64  
  118. #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")  
  119. #else  
  120. #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")  
  121. #endif  
  122. #endif  // _UNICODE  
  123. //  
  124. // END  
  125.   
  126. #endif  // _STDAFX_H_  

常用預處理指令^

VC 支持的預處理指令參考 MSDN: Preprocessor Directives

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#error 產生人工編譯錯誤^

#error 產生 fatal error,編譯器輸出 #error 后的提示文本。指示該源文件必需使用 C++ 方式編譯,如下:

  1. // test.cpp  
  2. #ifndef __cplusplus  
  3. #error MUST use C++ compilation  
  4. #endif  

以 C 語言方式編譯上面源文件 (/Tc test.cpp) 時報錯:

fatal error C1189: #error :  MUST use C++ compilation 

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#line 改變行號和源文件名^

#line 改變 __FILE__ 和 __LINE__ 的取值,例如:

  1. // 實際文件名:test_01.cpp  
  2. #line 1 "test_02.cpp"  
  3.     _tprintf(_T("File: %s, Line: %d\n"), _T(__FILE__), __LINE__);   // 實際第 200 行  

輸出:

File: test_02.cpp, Line: 1 

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif"># 空指令^

沒有作用的合法預處理指令行正則表達式:[\t ]*#[\t ]*

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma 預處理指令^

#pragma 是一組編譯器特定的預處理指令,每種編譯器的 #pragma 的子指令都有所不同。VC 的 #pragma 指令參考 MSDN: Pragma Directives and the __Pragma Keyword

常用的 #pragma 指令如下:

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma once 只包含一次頭文件^

對頭文件只包含一次,如下:

  1. // test.h  
  2. #if _MSC_VER > 1000  
  3. #pragma once  
  4. #endif  
  5.   
  6. // 頭文件中的代碼  

它和傳統的 #ifndef 只包含一次技巧的功能相同:

  1. // test.h  
  2. #ifndef _TEST_H_  
  3. #define _TEST_H_  
  4.   
  5. // 頭文件中的代碼  
  6.   
  7. #endif  // _TEST_H_  

在源文件中多次 #include 包含 test.h 時,不會出現 redefinition 錯誤

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma message 編譯時輸出消息^

#pragma message 在編譯過程中,向標准輸出或 VC 的 Output 窗口打印指定消息,作用:(1) 告知程序員代碼編譯和使用的注意事項 (2) 用於查看和診斷實際的編譯代碼

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:用 #pragma message 和 STRINGIZE 查看宏的展開結果^

例 11 是用 STRINGIZE 在運行時輸出宏的展開結果,其實在編譯時也可以用 #pragma message 輸出,診斷編譯的實際代碼:

  1. // 將輸出實際的行號、數字,而非字符串 "__LINE__"、"MAX_PATH"  
  2. #pragma message("Line: " _STRINGIZE(__LINE__))  
  3. #pragma message("MAX_PATH: " _STRINGIZE(MAX_PATH))  
  4.   
  5. // 判斷宏的當前值、調用了哪個版本的 _t 系列函數、Windows API  
  6. #pragma message("_DEBUG: " _STRINGIZE(_DEBUG) ", _UNICODE: " _STRINGIZE(_UNICODE))  
  7. #pragma message("_tprintf: " _STRINGIZE(_tprintf))  
  8. #pragma message("CreateFile: " _STRINGIZE(CreateFile))  

在標准輸出或 VC 的 Output 窗口輸出:

  1. Line: 209  
  2. MAX_PATH: 260  
  3. _DEBUG: 1, _UNICODE: 1  
  4. _tprintf: wprintf  
  5. CreateFile: CreateFileW  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma push_macro/pop_macro 保存和恢復宏定義^

#pragma push_macro/pop_macro 用來解決宏命名沖突問題,如下:

  1. // 保存來自 windef.h 的 MAX_PATH 定義  
  2. #pragma message("MAX_PATH: " _STRINGIZE(MAX_PATH))  
  3. #pragma push_macro("MAX_PATH")  
  4.   
  5. // 對 MAX_PATH 進行新的定義  
  6. // 即使之前沒有定義 MAX_PATH,#undef MAX_PATH 也不會報錯  
  7. #undef MAX_PATH  
  8. #define MAX_PATH    512  
  9. #pragma message("MAX_PATH: " _STRINGIZE(MAX_PATH))  
  10.   
  11. // 使用新的 MAX_PATH  
  12.   
  13. // 恢復 windef.h 的 MAX_PATH 定義  
  14. #pragma pop_macro("MAX_PATH")  
  15. #pragma message("MAX_PATH: " _STRINGIZE(MAX_PATH))  

#pragma message 輸出如下:

  1. MAX_PATH: 260  
  2. MAX_PATH: 512  
  3. MAX_PATH: 260  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma warning 禁用和啟用編譯警告^

例子:

  1. // 禁用 C4507、C4034 警告(VC 的 warning 編號從 C4001 開始)  
  2. // 只報告一次 C4385 警告  
  3. // 將 C4164 警告作為編譯錯誤  
  4. #pragma warning(disable: 4507 34; once: 4385; error: 164)  
  5.   
  6. #pragma warning(push)           // 保存當前的警告設置:全局警告級別和 disable 的  
  7. #pragma warning(disable: 4705)  // 禁用某些警告  
  8. #pragma warning(disable: 4706)  
  9. #pragma warning(disable: 4707)  
  10.   
  11. // 會產生 C4705、C4706、C4707 警告的代碼  
  12. #pragma warning(pop)            // 恢復保存的警告設置  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma comment 目標文件注釋和編譯選項傳遞^

#pragma comment 的作用是在編譯或鏈接過程中,向 COFF 二進制目標文件或 PE 可執行文件 (.obj/.lib/.exe/.dll) 中插入字符串注釋。目的:

(1). 將版本、版權等信息插入到 COFF/PE 文件中,以便發布
(2). 插入的字符串會作為后續編譯階段(如鏈接)的選項,以便支持如 auto-link 等在源碼中設置編譯、鏈接選項

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:用 #pragma comment(lib) 實現庫的 auto-link^

以例 17 DllProj 工程為基礎,DllProj 定義有 DLLPROJ_EXPORTS 宏,DllProj.h 如下:

  1. // DllProj.h  
  2.   
  3. #ifndef _DLLPROJ_H_  
  4. #define _DLLPROJ_H_  
  5.   
  6. #ifdef DLLPROJ_EXPORTS  
  7. #define DLLPROJ_API __declspec(dllexport)  
  8. #else  
  9. #pragma comment(lib, "DllProj.lib")     // 指示在導入符號時鏈接 DllProj.lib  
  10. #define DLLPROJ_API __declspec(dllimport)  
  11. #endif  
  12.   
  13. // 省略代碼  
  14.   
  15. #endif  // _DLLPROJ_H_  

在使用工程中 #include 該文件,並設置庫搜索路徑 /LIBPATH,不必指定鏈接導入庫 DllProj.lib

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma comment(linker) 傳遞鏈接選項^

#pragma comment(linker, "link_option")

link_option 只能為以下鏈接選項:

/DEFAULTLIB
/EXPORT
/INCLUDE
/MANIFESTDEPENDENCY
/MERGE
/SECTION

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma comment(linker, "/SECTION") 設置區段屬性^

設置區段屬性的方法有:

(1). 使用模塊定義文件 .def 的 SECTIONS 定義,見 MSDN: SECTIONS (C/C++)

(2). 使用 /SECTION 鏈接選項,見 MSDN: /SECTION (Specify Section Attributes)

(3). 使用 #pragma section 指令創建區段

對於上面的方法 (2),可用 #pragma comment(linker) 指定鏈接選項

例子:#pragma comment(linker, "/SECTION") 設置可讀寫、共享區段

  1. #pragma comment(linker, "/SECTION:.mydata,RWS")  
  2. #pragma data_seg(".mydata")  
  3.   
  4. // .mydata 區段中的變量定義  
  5.   
  6. #pragma data_seg()  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma 區段操作^

區段屬性和標准區段名參考 /SECTION 鏈接選項。查看區段偏移地址 (RVA)、起始地址和屬性,用 dumpbin 工具:

dumpbin /SECTION:secname xxx.exe|xxx.dll 

自定義區段名不能和標准區段名沖突。自定義區段名長度限制為 8 個字符,超過 8 個會截斷

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma section 在目標文件中創建區段^

#pragma section 在.obj 中創建區段,並設置屬性 read, write, execute, shared, nopage, nocache, discard, remove

#pragma section 新建的區段不包括任何內容,需向新建的區段放置數據或代碼:

(1). 對於數據區段(全局變量)

(a). __declspec(allocate("secname")) 修飾
(b). #pragma bss_seg/const_seg/data_seg 指令

(2). 對於代碼區段(函數)

(a). #pragma alloc_text 指令
(b). #pragma code_seg 指令

例子:#pragma section 創建可讀寫、共享區段

  1. #pragma section(".mydata", read, write, shared)     // 在 .obj 中新建可讀寫、共享區段 .mydata  
  2. // #pragma comment(linker, "/SECTION:.mydata,RWS")  // 作用與上類似:在鏈接時調整區段屬性  
  3. #pragma data_seg(".mydata")                         // 將以下初始化數據放置到 .mydata  
  4.   
  5. // .mydata 區段中的變量定義  
  6.   
  7. #pragma data_seg()                                  // 恢復默認的初始化數據區段 .data  
  8.   
  9. __declspec(allocate(".mydata"))                     // 用  __declspec(allocate) 放置數據到區段  
  10. int g_Var = 0;  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma alloc_text 將 C 鏈接約定的函數放置到區段^

#pragma alloc_text 只能應用於 C 鏈接約定的函數,即對於 C++ 編譯方式,需用 extern "C" 聲明函數。所以 #pragma alloc_text 不支持重載函數和類成員函數

#pragma alloc_text 在函數聲明與函數定義體之間使用

例子:在可執行、非分頁區段中,用 #pragma alloc_text 放置 C 鏈接約定函數

  1. extern "C" void TestFunc_1();                           // 必需在 .mycode 之前有函數聲明  
  2. extern "C" void TestFunc_2();                           // 並且是 C 鏈接約定的  
  3.   
  4. #pragma section(".mycode", read, execute, nopage)       // 建立可執行、非分頁區段  
  5. // #pragma comment(linker, "/SECTION:.mycode,RE!P")     // 作用與上類似:在鏈接時調整區段屬性  
  6. #pragma alloc_text(".mycode", TestFunc_1, TestFunc_2)   // 將指定函數放到 .mycode 中  
  7.   
  8. void TestFunc_1()  
  9. {  
  10.     // TestFunc_1 函數體  
  11. }  
  12.   
  13. void TestFunc_2()  
  14. {  
  15.     // TestFunc_2 函數體  
  16. }  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma code_seg 將函數放置到代碼區段^

#pragma code_seg/bss_seg/const_seg/data_seg 會新建區段,之前不必有 #pragma section;如果之前有 #pragma section,會使用 #pragma section 設置的區段屬性

如果省略參數,會放置到標准區段:

#pragma code_seg(): .text
#pragma data_seg(): .data
#pragma bss_seg(): .bss
#pragma const_seg(): .rdata

例子:在可執行、非分頁區段中,用 #pragma code_seg 放置函數

  1. #pragma section(".mycode", read, execute, nopage)       // 建立可執行、非分頁區段  
  2. // #pragma comment(linker, "/SECTION:.mycode,RE!P")     // 作用與上類似:在鏈接時調整區段屬性  
  3.   
  4. #pragma code_seg(".mycode")                             // 將以下函數放到 .mycode 區段中  
  5. void TestFunc_1()  
  6. {  
  7.     // TestFunc_1 函數體  
  8. }  
  9.   
  10. void TestFunc_2()  
  11. {  
  12.     // TestFunc_2 函數體  
  13. }  
  14. #pragma code_seg()                                      // 恢復默認的標准代碼區段 .text  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma data_seg/bss_seg/const_seg 將數據放置到區段^

數據性質和區段有對應關系,如果放置區段和數據性質沖突,則不會實際放到該區段中:

(1). .data 標准區段,放置初始化非 0 全局數據。用 #pragma data_seg 放置初始化數據,必需顯示初始化其中變量(可以初始化為 0),否則不會放入 #pragma data_seg 指定的區段

(2). .bss 標准區段,放置未初始化、默認或顯式初始化為 0 的全局數據。注意鏈接時 .bss 會向 .data 中合並,所以在 .exe/.dll 中看不到 .bss 區段,可查看 .obj 中的 .bss 區段。用 #pragma bss_seg 放置未初始化數據,必需不初始化其中變量(也不能初始化為 0),否則不會放入 #pragma bss_seg 指定的區段

(3). .rdata 標准區段,放置只讀的全局常量數據。const 數字類型會編碼到代碼中(指令立即數),所以不放到 .rdata 中。用 #pragma const_seg 放置只讀常量數據

例子:自定義區段和數據性質沖突

以下錯誤編譯器不會報錯,但實際沒有放置到期望的區段中

  1. int g_Var1 = 1;     // 放置到 .data 中  
  2.   
  3. int g_Var2 = 0;     // 放置到 .obj 的 .bss 中,鏈接時合並到 .data  
  4. int g_Var3;         // 同上  
  5.   
  6. const int g_Var4 = 1;           // 編碼到代碼中,沒有放置到 .rdata 中  
  7. const char g_szVar5[]= "foo";   // 放置到 .rdata 中  
  8.   
  9. #pragma const_seg(".myrdata")  
  10. const char g_szVar6[]= "foo";   // 放置到 .myrdata 中  
  11. #pragma const_seg()  
  12.   
  13. #pragma bss_seg(".mybss")  
  14. int g_Var7 = 1;     // 錯誤:放置到 .data 中  
  15. int g_Var8 = 0;     // 錯誤:放置到 .obj 的 .bss 中,鏈接時合並到 .data  
  16. int g_Var9;         // 正確放置到 .mybss 中  
  17. #pragma bss_seg()  
  18.   
  19. #pragma data_seg(".mydata")  
  20. int g_Var10 = 0;    // 正確放置到 .mydata 中  
  21. int g_Var11;        // 錯誤:放置到 .obj 的 .bss 中,鏈接時合並到 .data  
  22. #pragma data_seg()  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma pack 設置成員字節對齊^

設置 struct, union, class 成員字節對齊的方法:

(1). 編譯選項 /Zp,x86 缺省為 /Zp8,即以 8 byte 對齊,參考 MSDN: /Zp (Struct Member Alignment)

(2). 使用 __declspec(align(#)) 修飾,參考 MSDN: align (C++)

(3). 使用 #pragma pack

#pragma pack(): 不帶參數的 #pragma pack() 表示恢復到編譯選項 /Zp 設置的字節對齊

#pragma pack(show): 產生一條 C4810 編譯警告,報告當前的字節對齊:

Test.cpp(140) : warning C4810: value of pragma pack(show) == 8 

例子:使用 #pragma pack 設置成員字節對齊

  1. // x86 32bit  
  2.   
  3. #pragma pack(4)             // 用一對 #pragma pack(4) | #pragma pack()  
  4. // #pragma pack(push, 4)    // 用一對 #pragma pack(push, 4) | #pragma pack(pop)  
  5. struct TestStruct  
  6. {  
  7.     double  a;              // sizeof(double) = 8  
  8.     int     b;              // sizeof(int) = 4  
  9. };                          // sizeof(TestStruct) = 12  
  10.   
  11. // #pragma pack(4) 會一直作用,直到改變 pack   
  12.   
  13. #pragma pack()              // 恢復編譯選項 /Zp 設置的字節對齊  
  14. // #pragma pack(pop)        // 恢復 #pragma pack(push, 4) 之前的字節對齊  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma inline 函數設置^

inline 函數修飾,參考 MSDN: inline, __inline, __forceinline

inline 函數編譯優化選項,參考 MSDN: /Ob (Inline Function Expansion)

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma auto_inline 禁用和啟用 auto-inline^

例子:

使用 /O2 編譯優化選項,含 /Ob2:啟動 auto-inline

  1. #pragma auto_inline(off)  
  2. int simple_func(int a)      // 不會 inline 化  
  3.   
  4. inline                      // #pragma auto_inline(off) 不會作用於顯式指定的 inline 函數  
  5. int simple_func2(int a)     // 會 inline 化  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma inline_depth 設置函數調用的 inline 化深度^

#pragma inline_depth(n) 作用於 inline、__inline 和 /Ob2 選項下的 auto-inline 化函數,不作用於 __forceinline 函數。需要 /Ob1 或 /Ob2 編譯選項

n: 0 ~ 255,255 表示無限制調用深度 inline 化,0 表示禁止 inline 化,省略參數 #pragma inline_depth() 時 n = 254

遞歸函數 inline 化的最大調用深度為 16 次調用

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma inline_recursion 禁用和啟用遞歸函數的 inline 化^

作用於 inline、__inline 和 /Ob2 選項下的 auto-inline 化函數。需要 /Ob1 或 /Ob2 編譯選項

默認為 #pragma inline_recursion(off),這時一個可 inline 化的遞歸調用函數只 inline 展開一次。如果 #pragma inline_recursion(on),則 inline 展開深度由 #pragma inline_depth 限制,並不超過 16 次

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma 優化指令^

編譯優化選項 /O,參考 MSDN: /O Options (Optimize Code)

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma optimize 禁用或啟動特定優化^

#pragma optimize("gp(s|t)y", on|off)

優化參數和編譯優化選項之間的對應關系:

g: /Og
p: /fp:precise 浮點數一致性
s: /Os 生成最小代碼
t: /Ot 生成最快代碼
y: /Oy

  1. #pragma optimize("pt", on)  // 對下面的代碼使用 fp:precise, /Ot 優化  
  2.   
  3. // 函數定義  
  4.   
  5. #pragma optimize("", off)   // 關閉上次 #pragma optimize 指定的優化  
  6. #pragma optimize("", on)    // 恢復到編譯器 /O 選項指定的優化  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma intrinsic 使用 intrinsic 函數^

使用 intrinsic 函數編譯選項 /Oi,參考 MSDN: /Oi (Generate Intrinsic Functions)

#pragma intrinsic,參考 MSDN: intrinsic

  1. #include <string.h>  
  2.   
  3. // 使用 /Oi 編譯選項  
  4.   
  5. #pragma intrinsic(memcpy)  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma function 使用普通函數^

和 #pragma intrinsic 對應,改變 /Oi 選項或之前的 #pragma intrinsic 設置,使用指定函數名的普通函數版本

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma deprecated 聲明廢棄函數^

#pragma deprecated 用來聲明廢棄的函數、類型、宏,編譯器產生 C4995 警告

__declspec(deprecated) 修飾也可用來聲明廢棄的函數、類型,編譯器產生 C4996 警告

例子:

  1. // 使用 #pragma deprecated  
  2. // BEGIN  
  3. //  
  4. #pragma deprecated(OldClass)  
  5. class OldClass1;  
  6.   
  7. #pragma deprecated(test_func1)  
  8. void old_func1();  
  9. //  
  10. // END  
  11.   
  12. // 使用 __declspec(deprecated)  
  13. // BEGIN  
  14. #define DEPRECATED_WILL_RMOVED  "** will be removed in next version **"  
  15.   
  16. // deprecated() 中的字符串不是必需的,如果有,會在警告時輸出  
  17. __declspec(deprecated(DEPRECATED_WILL_RMOVED)) void old_func2();  
  18.   
  19. // 注意 __declspec(deprecated) 修飾 class 時的位置  
  20. class __declspec(deprecated) OldClass2;  
  21. //  
  22. // END  
  23.   
  24. void test()  
  25. {  
  26.     old_func1();    // 產生 C4995 警告  
  27.     OldClass1 obj;  // 產生 C4995 警告  
  28.   
  29.     old_func2();    // 產生 C4996 警告,並輸出 "** will be removed in next version **"  
  30.     OldClass2();    // 產生 C4996 警告  
  31. }  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma omp 使用 OpenMP 指令^

指令形式:

#pragma omp omp_directive 

用於多線程、並發編程的 OpenMP 指令,子指令 omp_directive 參考 MSDN: OpenMP Directives

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma region/endregion 折疊代碼塊^

標記一整塊代碼,在 VC 編輯器可折疊成一行 (+) 和展開,見 VC 的 Edit->Outlining 菜單

VC Outlining 常用快捷鍵:

Ctrl + M, Ctrl + L: 折疊或展開所有的代碼塊
Ctrl + M, Ctrl + M: 折疊或展開光標所在的代碼塊

  1. #pragma region FuncTestCode             // 折疊成一行后,(+) 后顯示的名字  
  2.   
  3. // 這里是一整塊代碼  
  4.   
  5. #pragma endregion Test Code of Func()   // 折疊后在名字后顯示的注釋  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma setlocale 設置源代碼中字符串字面量的編碼^

#pragma setlocale("locale")

#pragma setlocale() 使用的 locale 參數和 CRT 函數 setlocale() 的相同,參考 MSDN: Language Strings,如 簡體中文 "chs" (GBK),繁體中文 "cht" (BIG5),日文 "jpn" (JIS)。注意:GBK 包括簡體中文、繁體中文、日文,所以繁體中文的源文件不一定是 BIG5,也可能是 GBK,要看實際的編碼

例子:

默認源代碼的設置為 #pragma setlocale(""),"" 表示 Windows 用戶默認 ANSI 代碼頁,在控制面板中區域和語言選項中設置,默認簡體中文系統為 GBK,繁體中文系統為 BIG5 等。所以在簡體系統下編寫簡體字面量代碼,或在繁體系統下編寫繁體字面量代碼等,無需設置源文件的 #pragma setlocale

在簡體中文 Windows 下源文件使用 BIG5 編碼源文件,代碼中有 L"xxx" 的寬字符字面量,且 "xxx" 在 BIG5 - ASCII 的字符集范圍,則應當使用 #pragma setlocale("cht")

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma include_alias 定義頭文件別名^

  1. #pragma include_alias(<stdio.h>, <newstdio.h>)  
  2. #pragma include_alias("api.h", "test\api.h")  
  3.   
  4. #include <stdio.h>  
  5. #include "api.h"  

預處理相關編譯選項^

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">/D 定義宏^

/D: 定義宏,作用類似 #define,但會去掉選項中的引號
/U: 取消指定的預定義宏,類似 #undef,如 /U _DEBUG
/u: 取消所有的預定義宏。/U 和 /u 都不能取消在源碼中用 #define 定義的宏

定義數字^

  • /DTESTMACRO: 等價 #define TESTMACRO 1,整數
  • /DTESTMACRO=1: 同上
  • /DTESTMACRO="1": 同上
  • /DTESTMACRO=3.14: 等價 #define TESTMACRO 3.14,浮點數
  • /DTESTMACRO="3.14": 同上

定義字符串^

  • /DTESTMACRO="abcdef": 等價 #define TESTMACRO abcdef,非字符串字面量(沒有引號)
  • /DTESTMACRO=\"abcdef\": 等價 #define TESTMACRO "abcdef",字符串字面量
  • /DTESTMACRO="\"abcdef\"": 同上

空定義^

  • /DTESTMACRO=: 等價 #define TESTMACRO
  • /DTESTMACRO="": 同上
  • /DTESTMACRO=\"\": 等價 #define TESTMACRO "",非空定義,而是空字符串

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">CL 環境變量使用 /D^

SET CL=/DTESTMACRO#1: 用 # 代替 =,等價 /DTESTMACRO=1,即 #define TESTMACRO 1

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">/E, /EP, /P 預處理選項^

/E: 預處理源文件,結果輸出到標准輸出,去掉注釋,在 #inlcude 展開和條件編譯周圍產生 #line 行號指示
/EP: 和 /E 相似,結果輸出到標准輸出,但不產生 #line
/P: 和 /E 相似,產生 #line,結果輸出到文件 (test.cpp => test.i),相當於 cl /E test.cpp > test.i
/P /EP 聯用: 結果輸出到文件 test.i,且不產生 #line。/E、/EP、/P 不能和預編譯頭 PCH 聯用
/C: 預處理時保留注釋,和 /E、/P、/EP 聯用

例子:預處理展開源文件^

源文件 test.cpp:

  1. #include <stdio.h>  
  2.   
  3. int main()  
  4. {  
  5. #ifdef _DEBUG  
  6.     printf("Debug config\n");  
  7. #else  
  8.     printf("Release config\n");  
  9. #endif  
  10.   
  11. // MARK: TESTMACRO value  
  12.     printf("TESTMACRO: %d\n", TESTMACRO);  
  13.     return 0;  
  14. }  

預處理編譯命令:

cl /P /C /DTESTMACRO test.cpp 

預處理輸出到 test.i:

  1. #line 1 "test.cpp"  
  2. #line 1 "d:\\Visual Studio 8\\VC\\INCLUDE\\stdio.h"  
  3.   
  4. // #line 中 stdio.h 的路徑由實際 VC 安裝路徑而定  
  5. // 這里省略 stdio.h 展開后的大量代碼  
  6.   
  7. #line 706 "d:\\Visual Studio 8\\VC\\INCLUDE\\stdio.h"  
  8.   
  9. #line 2 "test.cpp"  
  10.   
  11. int main()  
  12. {  
  13.   
  14.   
  15.   
  16.     printf("Release config\n");  
  17. #line 10 "test.cpp"  
  18.   
  19. // MARK: TESTMACRO value  
  20.     printf("TESTMACRO: %d\n", 1);  
  21.     return 0;  
  22. }  

例子:過濾查看預處理展開結果^

用這種方法可以查看編譯過程中,實際的宏展開、預處理結果

以上面的 test.cpp 為例,預處理編譯命令和 grep 過濾:

cl /EP /C /DTESTMACRO test.cpp 2>NUL | egrep -A 5 -B 5 "MARK: TESTMACRO" 

2>NUL: 用於屏蔽輸出 VC 編譯器 banner 和提示、錯誤信息,用 /nologo 選項也可以
egrep -A 5 -B 5: 表示輸出匹配正則表達式前后 5 行

輸出結果如下:

  1.     printf("Release config\n");  
  2.   
  3. // MARK: TESTMACRO value  
  4.     printf("TESTMACRO: %d\n", 1);  
  5.     return 0;  
  6. }  

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">/showIncludes 輸出頭文件列表^

輸出源文件的 #include 的頭文件列表到 stderr,包括嵌套 #include

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:查看 #include 頭文件列表^

以上面 test.cpp 為例,編譯命令:

cl /nologo /showIncludes /EP test.cpp >NUL 

輸出頭文件列表,由實際 VC 安裝路徑而定。嵌套 #include 用空格縮進表示,如 stdio.h include=> crtdefs.h:

  1. test.cpp  
  2. Note: including file: d:\Visual Studio 8\VC\INCLUDE\stdio.h  
  3. Note: including file:  d:\Visual Studio 8\VC\INCLUDE\crtdefs.h  
  4. Note: including file:   d:\Visual Studio 8\VC\INCLUDE\sal.h  
  5. Note: including file:   d:\Visual Studio 8\VC\INCLUDE\vadefs.h  
  6. Note: including file:  d:\Visual Studio 8\VC\INCLUDE\swprintf.inl  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM