- C/C++ 預定義宏^
- CRT 和 C 標准庫中的宏^
- NULL 空指針^
- limits.h 整數類型常量^
- float.h 浮點類型常量^
- math.h 數學常量^
- EOF 常量^
- errno.h 錯誤代碼^
- locale 類別^
- _MAX_PATH 等文件名與路徑長度限制^
- RAND_MAX 隨機數最大值^
- va_arg/va_start/va_end 訪問變長函數參數^
- 宏實現的 CRT 函數^
- Microsoft 預定義宏^
- Windows API 中的注釋性宏^
- Windows API 中的常用宏^
- 字符串化操作符 #^
- 拼接操作符 ##^
- TCHAR 統一字符類型和處理^
- 宏的缺點和替代方法^
- 條件編譯^
- 預編譯頭文件^
- 常用預處理指令^
- #pragma 預處理指令^
- #pragma once 只包含一次頭文件^
- #pragma message 編譯時輸出消息^
- #pragma push_macro/pop_macro 保存和恢復宏定義^
- #pragma warning 禁用和啟用編譯警告^
- #pragma comment 目標文件注釋和編譯選項傳遞^
- #pragma 區段操作^
- #pragma pack 設置成員字節對齊^
- #pragma inline 函數設置^
- #pragma 優化指令^
- #pragma deprecated 聲明廢棄函數^
- #pragma omp 使用 OpenMP 指令^
- #pragma region/endregion 折疊代碼塊^
- #pragma setlocale 設置源代碼中字符串字面量的編碼^
- #pragma include_alias 定義頭文件別名^
- 預處理相關編譯選項^
作者:Breaker Zhao
轉載請注明作者和原文鏈接
VC 2005 中的宏 (#define) 與預處理 (#if/#ifdef/#pragma) 的使用方法總結。
關鍵字:宏, 預定義宏, 預處理, 預編譯頭, VC, #pragma, 編譯選項, 程序區段
- C/C++ 預定義宏
- CRT 和 C 標准庫中的宏
- Microsoft 預定義宏
- Windows API 中的注釋性宏
- Windows API 中的常用宏
- 字符串化操作符 #
- 拼接操作符 ##
- TCHAR 統一字符類型和處理
- 宏的缺點和替代方法
- 條件編譯
- 預編譯頭文件
- 常用預處理指令
- #pragma 預處理指令
- #pragma once 只包含一次頭文件
- #pragma message 編譯時輸出消息
- #pragma push_macro/pop_macro 保存和恢復宏定義
- #pragma warning 禁用和啟用編譯警告
- #pragma comment 目標文件注釋和編譯選項傳遞
- #pragma 區段操作
- #pragma pack 設置成員字節對齊
- #pragma inline 函數設置
- #pragma 優化指令
- #pragma deprecated 聲明廢棄函數
- #pragma omp 使用 OpenMP 指令
- #pragma region/endregion 折疊代碼塊
- #pragma setlocale 設置源代碼中字符串字面量的編碼
- #pragma include_alias 定義頭文件別名
- 預處理相關編譯選項
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++ 預定義宏的取值^
- // MacroTest.h
- void PrintSourceInfo()
- {
- const _TCHAR* pszstdc;
- const _TCHAR* pszcpp;
- #if __STDC__
- pszstdc = _T("YES");
- #else
- pszstdc = _T("NO");
- #endif
- #ifdef __cplusplus
- pszcpp = _T("YES");
- #else
- pszcpp = _T("NO");
- #endif
- _tprintf(_T("File: %s, Line: %d, Date: %s, Time: %s, Timestamp: %s, ANSI/ISO C: %s, C++: %s\n"),
- _T(__FILE__), __LINE__, _T(__DATE__), _T(__TIME__), _T(__TIMESTAMP__), pszstdc, pszcpp);
- }
- // 宏化的 PrintSourceInfo()
- #define PRINT_SOURCE_INFO() \
- _tprintf(_T("File: %s, Line: %d, Date: %s, Time: %s, Timestamp: %s\n"), \
- _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()
- // assert.h
- _CRTIMP void __cdecl _wassert(__in_z const wchar_t * _Message, __in_z const wchar_t *_File, __in unsigned _Line);
- #define assert(_Expression) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) )
- // crtdbg.h
- #define _ASSERT_EXPR(expr, msg) \
- (void) ((!!(expr)) || \
- (1 != _CrtDbgReportW(_CRT_ASSERT, _CRT_WIDE(__FILE__), __LINE__, NULL, msg)) || \
- (_CrtDbgBreak(), 0))
- #ifndef _ASSERT
- #define _ASSERT(expr) _ASSERT_EXPR((expr), NULL)
- #endif
- #ifndef _ASSERTE
- #define _ASSERTE(expr) _ASSERT_EXPR((expr), _CRT_WIDE(#expr))
- #endif
CRT 的調試輸出宏 _RPTn()/_RPTFn(),n: 0 ~ 5
_RPTWn()/_RPTFWn() 是寬字符版
- // crtdbg.h
- #define _RPT_BASE(args) \
- (void) ((1 != _CrtDbgReport args) || \
- (_CrtDbgBreak(), 0))
- #define _RPTF0(rptno, msg) \
- _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()
- // afx.h
- #define ASSERT(f) DEBUG_ONLY((void) ((f) || !::AfxAssertFailedLine(THIS_FILE, __LINE__) || (AfxDebugBreak(), 0)))
- #define ASSERT_VALID(pOb) DEBUG_ONLY((::AfxAssertValidObject(pOb, THIS_FILE, __LINE__)))
MFC 的調試輸出宏 TRACE()/TRACEn(),n: 0 ~ 3
- // atltrace.h
- #ifndef ATLTRACE
- #define ATLTRACE ATL::CTraceFileAndLineInfo(__FILE__, __LINE__)
- #define ATLTRACE2 ATLTRACE
- #endif
- // afx.h
- #include <atltrace.h>
- #define TRACE ATLTRACE
- #define THIS_FILE __FILE__
- #define VERIFY(f) ASSERT(f)
- #define DEBUG_ONLY(f) (f)
- #define TRACE0(sz) TRACE(_T("%s"), _T(sz))
- #define TRACE1(sz, p1) TRACE(_T(sz), p1)
- #define TRACE2(sz, p1, p2) TRACE(_T(sz), p1, p2)
- #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^
- // afx.h
- void* AFX_CDECL operator new(size_t nSize, LPCSTR lpszFileName, int nLine);
- #define DEBUG_NEW new(THIS_FILE, __LINE__)
- // 用戶代碼
- // 調試版 new
- #ifdef _DEBUG
- #define new DEBUG_NEW
- #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,如下:
- #ifdef __cplusplus
- #define NULL 0
- #else
- #define NULL ((void *)0)
- #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 是類型自動的^
- // baby pointer wrapper
- class Pointer
- {
- public:
- // 非 explicit 構造函數,說明 Pointer 可以從指針類型 void* 隱式轉換
- Pointer(void* p) : m_Ptr(p)
- {}
- bool IsNull() const
- {
- return (m_Ptr == NULL);
- }
- private:
- void* m_Ptr;
- };
- // 形參可以從指針類型 void* 隱式轉換
- void TestPointer(Pointer ptr)
- {
- _tprintf(_T("ptr is %sNULL\n"), ptr.IsNull() ? _T("") : _T("NOT "));
- }
- // 用戶代碼
- TestPointer(0); // OK,0 是類型自動的,0 被自動轉換為 void*,再次隱式轉換為 Pointer
- TestPointer(NULL); // OK,NULL 就是 0,同上
- TestPointer(1); // Error,C++ 中 1 不同於 0,它是確定的 int 類型,
- // 只能提升轉換到 float/double 類型,不能自動轉換為指針
- TestPointer((int*)1); // OK,強制轉換 1 為 int*,int* 自動轉換為 void*,再次隱式轉換為 Pointer
- // 注意: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
例子:浮點數極限值:判斷浮點數是否相等^
- // 對比一個 double 是否為 0
- inline
- bool double_equal0(double n)
- {
- return (n >= 0 ? n < DBL_MIN : n > -DBL_MIN);
- }
- // 對比兩個 double 是否相等
- inline
- bool double_equal(double l, double r)
- {
- return (l >= r ? l - r < DBL_EPSILON : r - l < DBL_EPSILON);
- }
- // 打印函數的結果
- #define TEST_BOOL_FUNC(func) _tprintf(_T("%s: %s\n"), _TSTRINGIZE(func), func ? _T("TRUE") : _T("FALSE"))
- // 用戶代碼
- // 對比 double 是否為 0 時,double_equal0() 更精確
- // 對比兩個 double 是否相等時,最好用 double_equal()
- TEST_BOOL_FUNC(double_equal0(0)); // TRUE
- TEST_BOOL_FUNC(double_equal0(DBL_EPSILON)); // FALSE
- TEST_BOOL_FUNC(double_equal0(-DBL_EPSILON)); // FALSE
- TEST_BOOL_FUNC(double_equal0(DBL_MIN)); // FALSE
- TEST_BOOL_FUNC(double_equal0(-DBL_MIN)); // FALSE
- TEST_BOOL_FUNC(double_equal(0, 0)); // TRUE
- TEST_BOOL_FUNC(double_equal(DBL_EPSILON, 0)); // FALSE
- TEST_BOOL_FUNC(double_equal(DBL_MIN, 0)); // TRUE
- TEST_BOOL_FUNC(double_equal(1.0, 1.0 + DBL_EPSILON)); // FALSE
- TEST_BOOL_FUNC(double_equal(1.0, 1.0 - DBL_EPSILON)); // FALSE
- TEST_BOOL_FUNC(double_equal(1.0, 1.0 + DBL_MIN)); // TRUE
- 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:
- #define _USE_MATH_DEFINES
- #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^
- // 設置 locale
- // 定義寬字符流與控制台 I/O 字符之間的轉換字符集編碼為系統 ANSI 字符集
- // 這樣在中文 Windows 上可輸入、顯示中文字符
- _tsetlocale(LC_ALL, _T(""));
- // 要用存儲空間 >= _gettchar() 返回值類型的變量保存其返回值
- // 而不要用 char ch = _getchar(),那樣會截斷其返回值類型
- int ch;
- while ((ch = _gettchar()) != _TEOF)
- _tprintf(_T("[%c]"), (_TCHAR)ch);
- _tprintf(_T("\nread stdin: %s\n"), (feof(stdin) ? _T("EOF") : _T("Error")));
測試輸出,用 Ctrl + Z 產生 EOF 信號:
- abc漢字
- [a][b][/c][漢][字][
- ]^Z
- 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 產生某個范圍內的隨機數^
- template<bool seed, typename Type>
- inline
- Type get_rand(Type min, Type max)
- {
- _ASSERT(max >= min);
- if (seed) // Release 方式編譯時,這個判斷語句會被優化掉
- srand((unsigned int) time(NULL));
- return (Type) (((double) rand() / (double) RAND_MAX) * (max - min) + min);
- }
- template<typename Type>
- inline
- Type get_rand_seed(Type min, Type max)
- {
- return get_rand<true>(min, max);
- }
- template<typename Type>
- inline
- Type get_rand_noseed(Type min, Type max)
- {
- return get_rand<false>(min, max);
- }
- // 用戶代碼
- #define RANGE_MIN 10
- #define RANGE_MAX 100
- int randnum;
- randnum = get_rand_seed(RANGE_MIN, RANGE_MAX);
- 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 版本號
工程配置管理類^
_DEBUG, NDEBUG: 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__ 用法:
- // 針對參數不為 void,且需要保存返回值的函數
- #define CALL_TRACE(func, ret, ...) { _tprintf(_T("call: %s\n"), _TSTRINGIZE(func)); ret = func(__VA_ARGS__); }
- // 針對返回值為 void 或不關心返回值的函數
- #define CALL_TRACE_VOID(func, ...) { _tprintf(_T("call: %s\n"), _TSTRINGIZE(func)); func(__VA_ARGS__); }
- // 針對參數為 void 的函數
- // NOTE: 函數 func() 使用 func(__VA_ARGS__) 展開時,會影響前面的變長參數函數 _tprintf(),
- // 導致運行時緩沖區訪問違例(Debug 方式產生保護中斷),所以不能用前兩版帶 func(__VA_ARGS__) 的 CALL_TRACE
- #define CALL_TRACE_VOIDPARM(func, ret) { _tprintf(_T("call: %s\n"), _TSTRINGIZE(func)); ret = func(); }
- // 針對返回值、參數均為 void 的函數
- #define CALL_TRACE_VOID_VOIDPARM(func) { _tprintf(_T("call: %s\n"), _TSTRINGIZE(func)); func(); }
- // 用戶代碼
- // Unicode 方式編譯時,輸出 call: CreateFileW,並將返回值傳給 hFile
- 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^
- namespace std
- {
- typedef std::basic_string<_TCHAR> _tstring;
- }
- #define FORMAT_STRING(str, buf, sz, ...) { sprintf_s(buf, sz, __VA_ARGS__); str = buf; }
- #define FORMAT_WSTRING(str, buf, sz, ...) { swprintf_s(buf, sz, __VA_ARGS__); str = buf; }
- #define FORMAT_TSTRING(str, buf, sz, ...) { _stprintf_s(buf, sz, __VA_ARGS__); str = buf; }
- // 用戶代碼
- _TCHAR buf[512];
- _tstring str;
- 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 開始:
- // 保證 MAKE_MASK 在所有其它使用 __COUNTER__ 代碼之前,這樣才能
- // 保證第一次 MAKE_MASK 時,產生 2 << 0
- #define MAKE_MASK0(maskname) maskname = 1
- #define MAKE_MASK(maskname) maskname = (2 << __COUNTER__) // 說明 __COUNTER__ 是支持嵌套展開的
- // 用戶代碼
- enum MyMask
- {
- MAKE_MASK0(MASK_0), // 2^0: 1
- MAKE_MASK(MASK_1), // 2^1: 2 << 0
- MAKE_MASK(MASK_2), // 2^2: 2 << 1
- MAKE_MASK(MASK_3), // 2^3: 2 << 2
- MAKE_MASK(MASK_4) // 2^4: 2 << 3
- // 最大 MASK = MASK_31 2^31: 2 << 30
- };
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:用 __FUNCTION__ 打印跟蹤函數調用^
- #define BEGIN_FUNC _tprintf(_T("%s BEGIN\n"), _T(__FUNCTION__));
- #define END_FUNC _tprintf(_T("%s END\n"), _T(__FUNCTION__));
- // 用戶代碼
- void test_funcname_macro()
- {
- BEGIN_FUNC
- // 函數的功能代碼
- END_FUNC
- }
" 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() 的聲明:
- // WinBase.h
- WINBASEAPI
- __out
- HANDLE
- WINAPI
- CreateFileW(
- __in LPCWSTR lpFileName,
- __in DWORD dwDesiredAccess,
- __in DWORD dwShareMode,
- __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
- __in DWORD dwCreationDisposition,
- __in DWORD dwFlagsAndAttributes,
- __in_opt HANDLE hTemplateFile
- );
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">Windows API 中的常用宏^
Windows API 包含大量旗標、掩碼、狀態碼、錯誤碼等常量式宏
函數式宏最常用的有:
類型輔助類^
- BYTE HIBYTE(WORD wValue)
- BYTE LOBYTE(WORD wValue)
- WORD HIWORD(DWORD dwValue)
- WORD LOWORD(DWORD dwValue)
- WORD MAKEWORD(BYTE bLow, BYTE bHigh)
- LONG MAKELONG(WORD wLow, WORD wHigh)
- LRESULT MAKELRESULT(WORD wLow, WORD wHigh)
- LPARAM MAKELPARAM(WORD wLow, WORD wHigh)
- WPARAM MAKEWPARAM(WORD wLow, WORD wHigh)
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">GDI 類^
- DWORD MAKEROP4(DWORD fore, DWORD back): used in MaskBlt()
- LONG DIBINDEX(WORD wColorTableIndex)
- COLORREF PALETTEINDEX(WORD wPaletteIndex)
- COLORREF PALETTERGB(BYTE bRed, BYTE bGreen, BYTE bBlue)
- COLORREF RGB(BYTE byRed, BYTE byGreen, BYTE byBlue)
- BYTE GetBValue(DWORD rgb)
- BYTE GetGValue(DWORD rgb)
- BYTE GetRValue(DWORD rgb)
- POINTS MAKEPOINTS(DWORD dwValue)
另外,BITMAP_WIDTHBYTES(bits) 不在 Windows API 中,但比較常用於位圖:
- // 輸入:位圖圖像中一行的邏輯位數 = 位圖像素寬 x 每像素位數
- // 輸出:位圖圖像中一行占用的字節數,按 4 Bytes 對齊
- #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 中定義
狀態碼有關的宏:
- MAKE_HRESULT(sev, fac, code): 將 severity、facility、code 合並為 HRESULT
- HRESULT_CODE(hr): 取得 HRESULT 的 code 部分
- HRESULT_FACILITY(hr): 取得 HRESULT 的 facility 部分
- HRESULT_SEVERITY(hr): 取得 HRESULT 的 severity 位
- HRESULT_FROM_NT(nt_stat): 從 NTSTATUS 變換到 HRESULT
- HRESULT_FROM_WIN32(win_err): 從 Win32 狀態碼變換到 HRESULT
- SUCCEEDED(hr): HRESULT 是否表示成功
- FAILED(hr): HRESULT 是否表示失敗
- IS_ERROR(hr): HRESULT 是否表示一個錯誤
Win32 狀態碼沒有類似 MAKE_HRESULT 的宏,自定義 Win32 狀態碼時可以用 mc (Message Compiler) 工具處理 .mc 腳本,自動生成含自定義 Win32 狀態碼的頭文件,同時生成用於 FormatMessage() 的狀態碼文本描述,參考 MSDN:Message Compiler
也可以自定義用於 Win32 狀態碼的 MAKE_WINERR():
- // copy from WinError.h
- //
- // Values are 32 bit values layed out as follows:
- //
- // 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
- // 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
- // +---+-+-+-----------------------+-------------------------------+
- // |Sev|C|R| Facility | Code |
- // +---+-+-+-----------------------+-------------------------------+
- //
- // where
- //
- // Sev - is the severity code
- //
- // 00 - Success
- // 01 - Informational
- // 10 - Warning
- // 11 - Error
- //
- // C - is the Customer code flag
- //
- // R - is a reserved bit
- //
- // Facility - is the facility code
- //
- // Code - is the facility's status code
- //
- // Win32 狀態碼的各部分起始位、位掩碼和位長度
- #define WINERR_SEVERITY_BIT_LOW 30
- #define WINERR_SEVERITY_MASK 0xC0000000
- #define WINERR_SEVERITY_BIT_LEN 2
- #define WINERR_SEVERITY_VALUE(val) (((val) << WINERR_SEVERITY_BIT_LOW) & WINERR_SEVERITY_MASK)
- #define WINERR_CUSTOM_DEFINE_BIT_LOW 29
- #define WINERR_CUSTOM_DEFINE_MASK 0x20000000
- #define WINERR_CUSTOM_DEFINE_BIT_LEN 1
- #define WINERR_CUSTOM_DEFINE_FLAG (1 << WINERR_CUSTOM_DEFINE_BIT_LOW)
- #define WINERR_FACILITY_BIT_LOW 16
- #define WINERR_FACILITY_MASK 0x0FFF0000
- #define WINERR_FACILITY_BIT_LEN 12
- #define WINERR_FACILITY_VALUE(val) (((val) << WINERR_FACILITY_BIT_LOW) & WINERR_FACILITY_MASK)
- #define WINERR_CODE_BIT_LOW 0
- #define WINERR_CODE_MASK 0x0000FFFF
- #define WINERR_CODE_BIT_LEN 16
- #define WINERR_CODE_VALUE(val) (val) & WINERR_CODE_MASK
- // Win32 狀態碼中的嚴重級別 severity
- #define WINERR_SEVERITY_SUCCESS 0
- #define WINERR_SEVERITY_INFORM 1
- #define WINERR_SEVERITY_WARNING 2
- #define WINERR_SEVERITY_ERROR 3
- #define WINERR_SEVERITY_NOT_CARE 3
- // 自定義 Win32 狀態碼的宏
- #define MAKE_WINERR(sev, fac, code) \
- ((DWORD)(WINERR_SEVERITY_VALUE(sev) | WINERR_CUSTOM_DEFINE_FLAG | WINERR_FACILITY_VALUE(fac) | WINERR_CODE_VALUE(code)))
調用規范類^
調用規范/約定參考 MSDN: Calling Conventions
Windows API 使用的調用規范名稱宏,在 WinDef.h 中定義:
- #define CALLBACK __stdcall
- #define WINAPI __stdcall
- #define WINAPIV __cdecl
- #define APIENTRY WINAPI
- #define APIPRIVATE __stdcall
- #define PASCAL __stdcall
COM 常用的調用規范輔助宏:
- EXTERN_C: C 鏈接約定
- STDAPI: __stdcall,C 鏈接約定,返回 HRESULT
- STDAPI_(type): __stdcall,C 鏈接約定,返回 type 類型
- STDMETHOD(method): __stdcall,返回 HRESULT 的類成員虛函數
- STDMETHOD_(type, method): __stdcall,返回 type 類型的類成員虛函數
- STDMETHODIMP: __stdcall,返回 HRESULT,對應 STDMETHOD(method) 實現
- STDMETHODIMP_(type): __stdcall,返回 type 類型,對應 STDMETHOD_(type, method) 實現
國際化類^
- WORD LANGIDFROMLCID(LCID lcid)
- WORD MAKELANGID(USHORT primaryLang, USHORT subLang)
- DWORD MAKELCID(WORD langID, WORD sortID)
- DWORD MAKESORTLCID(WORD langID, WORD sortID, WORD sortVersion)
- WORD PRIMARYLANGID(WORD lgid)
- WORD SORTIDFROMLCID(LCID lcid)
- WORD SORTVERSIONFROMLCID(LCID lcid)
- WORD SUBLANGID(WORD lgid)
資源類^
- LPTSTR MAKEINTRESOURCE(WORD wInt)
- BOOL IS_INTRESOURCE(WORD wInt)
網絡類^
- LPARAM MAKEIPADDRESS(BYTE b0, BYTE b1, BYTE b2, BYTE b3)
- BYTE FIRST_IPADDRESS(LPARAM lParam)
- BYTE SECOND_IPADDRESS(LPARAM lParam)
- BYTE THIRD_IPADDRESS(LPARAM lParam)
- BYTE FOURTH_IPADDRESS(LPARAM lParam)
- 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^
- #define __STRINGIZE(x) # x
- #define _STRINGIZE(x) __STRINGIZE(x)
- #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() 的定義:
- // crtdefs.h
- #ifndef _CRT_STRINGIZE
- #define __CRT_STRINGIZE(_Value) #_Value
- #define _CRT_STRINGIZE(_Value) __CRT_STRINGIZE(_Value)
- #endif
- #ifndef _CRT_WIDE
- #define __CRT_WIDE(_String) L ## _String
- #define _CRT_WIDE(_String) __CRT_WIDE(_String)
- #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():
- // 將輸出實際的行號、數字,而非字符串 "__LINE__"、"MAX_PATH"
- _tprintf(_T("Line: %s\n"), _TSTRINGIZE(__LINE__));
- _tprintf(_T("MAX_PATH: %s\n"), _TSTRINGIZE(MAX_PATH));
- // 判斷宏的當前值、調用了哪個版本的 _t 系列函數、Windows API
- _tprintf(_T("_DEBUG: %s, _UNICODE: %s\n"), _TSTRINGIZE(_DEBUG), _TSTRINGIZE(_UNICODE));
- _tprintf(_T("_tprintf: %s\n"), _TSTRINGIZE(_tprintf));
- _tprintf(_T("CreateFile: %s\n"), _TSTRINGIZE(CreateFile));
輸出結果:
- Line: 24
- MAX_PATH: 260
- _DEBUG: 1, _UNICODE: 1
- _tprintf: wprintf
- CreateFile: CreateFileW
2. 如果 _STRINGIZE() 的參數單純的變量、函數、類型、const、enum 常量,那么只是將 _STRINGIZE() 括號中的東西加引號而已,如下:
- // 非 const、其它內部類型 double、char,結果都一樣
- const int val = 260;
- // 枚舉常量
- enum MUSIC_STATE
- {
- ST_STOP,
- ST_PLAY,
- ST_PAUSE,
- ST_BUTT
- };
- // 自定義結構、類
- ClassTest obj;
- // 函數
- void func(int a);
- // 下面輸出 _TSTRINGIZE() 括號中名字加上引號得到的字符串,而非實際變量值
- _tprintf(_T("int: %s, val: %s\n"), _TSTRINGIZE(int), _TSTRINGIZE(val));
- _tprintf(_T("MUSIC_STATE: %s, ST_STOP: %s\n"), _TSTRINGIZE(MUSIC_STATE), _TSTRINGIZE(ST_STOP));
- _tprintf(_T("ClassTest: %s, obj: %s\n"), _TSTRINGIZE(ClassTest), _TSTRINGIZE(obj));
- _tprintf(_T("func: %s\n"), _TSTRINGIZE(func));
輸出結果:
- int: int, val: val
- MUSIC_STATE: MUSIC_STATE, ST_STOP: ST_STOP
- ClassTest: ClassTest, obj: obj
- func: func
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">拼接操作符 ##^
將代碼中兩個名字拼接到一起,形成一個名字。## 操作“不加引號”,參考 MSDN: Token-Pasting Operator
- #define __CONCAT(x, y) x ## y
- #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() 的定義^
- // tchar.h
- #ifdef _UNICODE
- #define __T(x) L ## x
- #else
- #define __T(x) x
- #define _T(x) __T(x)
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:Windows API 通用句柄類型的定義^
- // winnt.h
- typedef void *HANDLE;
- #define DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name
- // 因此多數 Windows 句柄是指向樁結構的指針,如 HWND:
- // windef.h
- DECLARE_HANDLE (HWND);
- // HWND 定義展開后是:
- struct HWND__
- {
- int unused;
- };
- typedef struct HWND__ *HWND;
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:用 ## 構造函數名^
- // 音樂播放狀態常量
- enum MUSIC_STATE
- {
- ST_STOP,
- ST_PLAY,
- ST_PAUSE,
- ST_BUTT
- };
- // 音樂播放狀態結構
- // 里面有一個用於處理特定狀態的回調函數 stat_proc
- typedef struct _MusicState
- {
- MUSIC_STATE stat;
- const _TCHAR* stat_name;
- int (*stat_proc)(void*);
- } MusicState;
- // 處理特定音樂播放狀態的函數
- // 函數名的統一形式 proc_ ## stat,stat 是狀態常量的名字
- int proc_ST_STOP(void*);
- int proc_ST_PLAY(void*);
- int proc_ST_PAUSE(void*);
- // 初始化音樂播放狀態結構
- #define INIT_MUSIC_STATE(stat) {stat, _TSTRINGIZE(stat), proc_ ## stat}
- MusicState g_MusicState[ST_BUTT] =
- {
- INIT_MUSIC_STATE(ST_STOP),
- INIT_MUSIC_STATE(ST_PLAY),
- INIT_MUSIC_STATE(ST_PAUSE)
- };
" 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 Mappings, Using 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 時才會分配內存):
- const char* DEF_FILENAME = "default.txt";
- enum BUF_SIZE
- {
- BUF_SIZE_SMALL = 64,
- BUF_SIZE_MEDIUM = 256,
- BUF_SIZE_LARGE = 1024
- };
另外,在 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). 宏體為多行語句,如果放到判斷語句中,並且不加 {} 包起來,只有第一句在判斷語句下執行,其它在判斷語句外,如下例:
- #define SWAP(v1, v2, tmp) \
- tmp = v1; \
- v1 = v2; \
- v2 = tmp;
- // 用戶代碼
- if (condition)
- SWAP(a, b, t); // 邏輯問題
- if (condition) {
- SWAP(a, b, t); // OK
- }
解決方法:定義宏時用 {} 或 do {} while(0) 包起來,如下:
- #define SWAP(v1, v2, tmp) \
- do { \
- tmp = v1; \
- v1 = v2; \
- v2 = tmp; \
- } 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 解決。如下:
- class TestClass1
- {
- private:
- int m_Val;
- // private 限制對宏 MACRO_DEF_VAL 不起作用
- #define MACRO_DEF_VAL 128
- public:
- static const int CONST_DEF_VAL = 128;
- enum { ENUM_DEF_VAL = 128 };
- };
- class TestClass2
- {
- private:
- int m_Val;
- // 產生 C4005 警告:MACRO_DEF_VAL 被重復定義
- #define MACRO_DEF_VAL 256
- public:
- static const int CONST_DEF_VAL = 256;
- enum { ENUM_DEF_VAL = 256 };
- };
- // 用戶代碼
- // 宏 MACRO_DEF_VAL 是全局的,不能寫為 TestClass1::MACRO_DEF_VAL
- _tprintf(_T("TestClass1: %d, %d, %d\n"), MACRO_DEF_VAL, TestClass1::CONST_DEF_VAL, TestClass1::ENUM_DEF_VAL);
- _tprintf(_T("TestClass2: %d, %d, %d\n"), MACRO_DEF_VAL, TestClass2::CONST_DEF_VAL, TestClass2::ENUM_DEF_VAL);
輸出結果:
后面定義的宏 MACRO_DEF_VAL 的值將前面的覆蓋了:
- TestClass1: 256, 128, 128
- 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
例子:注釋大量代碼^
- #if 0
- XXXXXXXXX
- #endif
- #if FALSE
- XXXXXXXXX
- #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
- // FrameWindow、Doc、View 等類均可覆蓋以下用於調試的診斷函數
- // 然后可以用 ASSERT_VALID() 診斷其對象狀態是否有效
- #ifdef _DEBUG
- virtual void AssertValid() const;
- virtual void Dump(CDumpContext& dc) const;
- #endif
- // Release 版本的 GetDocument() 是 inline 的
- #ifndef _DEBUG
- inline CMyDoc* CMyView::GetDocument() const
- {
- return reinterpret_cast<CMyDoc*>(m_pDocument);
- }
- #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 該文件:
- // DllProj.h
- #ifndef _DLLPROJ_H_
- #define _DLLPROJ_H_
- #ifdef DLLPROJ_EXPORTS
- #define DLLPROJ_API __declspec(dllexport)
- #else
- #define DLLPROJ_API __declspec(dllimport)
- #endif
- #ifdef __cplusplus
- #define EXTERN_C extern "C"
- #define EXTERN_C_BEGIN extern "C" {
- #define EXTERN_C_END }
- #else // __cplusplus defined
- #define EXTERN_C extern
- #define EXTERN_C_BEGIN
- #define EXTERN_C_END
- #endif // __cplusplus NOT defined
- // 導出類
- class DLLPROJ_API TestClass
- {
- public:
- TestClass();
- };
- // 導出全局變量,以 C 的鏈接方式(修飾名、調用約定)
- EXTERN_C DLLPROJ_API int g_TestVal;
- // 導出函數
- DLLPROJ_API int TestFunc();
- #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 以及使用方法如下:
- // undefine_WXDLLIMPEXP_CORE.h
- // 不要用 #pragma once 等包含一次技巧,因為一個源文件中可能有多個
- // BEGIN_DECLARE_EVENT_TYPES 自定義事件塊,這時要多次包含本文件
- #ifdef WXDLLIMPEXP_CORE
- # define REMOVE_WXDLLIMPEXP_CORE
- # undef WXDLLIMPEXP_CORE // 先取消 WXDLLIMPEXP_CORE 定義
- # define WXDLLIMPEXP_CORE // 再將其定義為空
- #endif
- // redefine_WXDLLIMPEXP_CORE.h
- // 不要用 #pragma once 等包含一次技巧
- #ifdef REMOVE_WXDLLIMPEXP_CORE
- # undef WXDLLIMPEXP_CORE
- // 以下塊拷貝自 wx-2.8.10 dlimpexp.h,用於恢復 WXDLLIMPEXP_CORE 的原有定義
- // BEGIN
- # ifdef WXMAKINGDLL_CORE
- # define WXDLLIMPEXP_CORE WXEXPORT
- # define WXDLLIMPEXP_DATA_CORE(type) WXEXPORT type
- # elif defined(WXUSINGDLL)
- # define WXDLLIMPEXP_CORE WXIMPORT
- # define WXDLLIMPEXP_DATA_CORE(type) WXIMPORT type
- # else /* not making nor using DLL */
- # define WXDLLIMPEXP_CORE
- # define WXDLLIMPEXP_DATA_CORE(type) type
- # endif
- // END
- # undef REMOVE_WXDLLIMPEXP_CORE
- #endif
- // 用戶代碼
- // 定義 WXDLLIMPEXP_CORE 為空
- #include <undefine_WXDLLIMPEXP_CORE.h>
- // 自定義事件
- BEGIN_DECLARE_EVENT_TYPES()
- DECLARE_EVENT_TYPE(wxEVENT_TEST_TRIGGERED, wxID_ANY)
- // DECLARE_LOCAL_EVENT_TYPE(wxEVENT_TEST_TRIGGERED, wxID_ANY) // 用這個不會有 BUG
- END_DECLARE_EVENT_TYPES()
- // 重定義 WXDLLIMPEXP_CORE
- #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 代碼^
- ////////////////////////////////////////////////////////////////////////////////
- ///
- /// @file stdafx.h
- /// @brief Windows 標准預編譯頭文件
- ///
- /// 將標准庫、運行時庫、基本庫、Windows API、第三方庫的頭文件在這里包含,生成
- /// 預編譯頭文件 MFCBasic.pch
- ///
- /// 如果不修改 stdafx.h,增量編譯時便不會重新編譯 stdafx.h 中包含的頭文件,這樣
- /// 加快了編譯速度
- ///
- /// @version <version>
- /// @author <author>
- /// @date 2011-07
- ///
- /// Copyright (c) 2011, <company>
- /// All rights reserved.
- ///
- ////////////////////////////////////////////////////////////////////////////////
- // 典型的“只包含一次”條件編譯技巧
- // VC cl 編譯器版本 10 以上 (_MSC_VER > 1000) 也可以使用 #pragma once 指令
- #ifndef _STDAFX_H_
- #define _STDAFX_H_
- // 排除很少使用的 Windows 頭文件
- #define WIN32_LEAN_AND_MEAN // 適用於 Windows API
- #ifndef VC_EXTRALEAN
- #define VC_EXTRALEAN // 適用於 MFC
- #endif
- // 指定目標系統和環境 (Windows, IE) 的版本號
- #ifndef WINVER
- #define WINVER 0x0501 // 目標系統具有 Windows XP 及以上特性
- #endif
- #ifndef _WIN32_WINNT
- #define _WIN32_WINNT 0x0501 // 目標系統具有 Windows XP 及以上特性
- #endif
- #ifndef _WIN32_WINDOWS
- #define _WIN32_WINDOWS 0x0410 // 目標系統具有 Windows 98 及以上特性
- #endif
- #ifndef _WIN32_IE
- #define _WIN32_IE 0x0600 // 目標系統具有 IE 6.0 及以上特性
- #endif
- ////////////////////////////////////////////////////////////////////////////////
- /// Include Header
- ////////////////////////////////////////////////////////////////////////////////
- // C 標准庫與運行時庫 (CRT)
- // BEGIN
- //
- #define _CRT_SECURE_NO_DEPRECATE // 使用廢棄 (deprecated) 的 CRT 函數時,不產生編譯警告
- #define _CRT_SECURE_NO_WARNINGS // 典型的廢棄函數有不帶緩沖區大小檢查的 strcpy()、strcat()、sprintf() 等
- #include <stdlib.h>
- #include <tchar.h>
- #include <crtdbg.h>
- #include <string.h>
- //
- // END
- // C++ 標准庫
- // BEGIN
- //
- #include <exception>
- #include <typeinfo>
- //
- // END
- // MFC 庫
- // BEGIN
- //
- #ifndef _SECURE_ATL
- #define _SECURE_ATL 1 // ATL/MFC 的安全設置
- #endif
- #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // 使 ATL/MFC 的 CString 具有顯式地構造函數 (explicit)
- #define _AFX_ALL_WARNINGS // 打開 MFC 的所有警告,包括一般可以安全忽略的警告
- #include <afxwin.h> // MFC 核心和標准支持
- #include <afxext.h> // MFC 擴展支持
- #ifndef _AFX_NO_OLE_SUPPORT
- #include <afxdtctl.h> // MFC 的 IE 4 通用控件支持
- #endif
- #ifndef _AFX_NO_AFXCMN_SUPPORT
- #include <afxcmn.h> // MFC 的 Windows 通用控件支持
- #endif
- //
- // END
- // Windows API
- // BEGIN
- //
- // #include <Windows.h> // 使用 MFC 庫時不要包含 Windows.h,MFC 頭文件中已包含
- #include <Winsock2.h>
- //
- // END
- // Windows 通用控件 ComCtl32.dll 版本 6.0 的內嵌 manifest
- // BEGIN
- //
- #ifdef _UNICODE
- #if defined _M_IX86
- #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
- #elif defined _M_IA64
- #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"")
- #elif defined _M_X64
- #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
- #else
- #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
- #endif
- #endif // _UNICODE
- //
- // END
- #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++ 方式編譯,如下:
- // test.cpp
- #ifndef __cplusplus
- #error MUST use C++ compilation
- #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__ 的取值,例如:
- // 實際文件名:test_01.cpp
- #line 1 "test_02.cpp"
- _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 只包含一次頭文件^
對頭文件只包含一次,如下:
- // test.h
- #if _MSC_VER > 1000
- #pragma once
- #endif
- // 頭文件中的代碼
它和傳統的 #ifndef 只包含一次技巧的功能相同:
- // test.h
- #ifndef _TEST_H_
- #define _TEST_H_
- // 頭文件中的代碼
- #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 輸出,診斷編譯的實際代碼:
- // 將輸出實際的行號、數字,而非字符串 "__LINE__"、"MAX_PATH"
- #pragma message("Line: " _STRINGIZE(__LINE__))
- #pragma message("MAX_PATH: " _STRINGIZE(MAX_PATH))
- // 判斷宏的當前值、調用了哪個版本的 _t 系列函數、Windows API
- #pragma message("_DEBUG: " _STRINGIZE(_DEBUG) ", _UNICODE: " _STRINGIZE(_UNICODE))
- #pragma message("_tprintf: " _STRINGIZE(_tprintf))
- #pragma message("CreateFile: " _STRINGIZE(CreateFile))
在標准輸出或 VC 的 Output 窗口輸出:
- Line: 209
- MAX_PATH: 260
- _DEBUG: 1, _UNICODE: 1
- _tprintf: wprintf
- 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 用來解決宏命名沖突問題,如下:
- // 保存來自 windef.h 的 MAX_PATH 定義
- #pragma message("MAX_PATH: " _STRINGIZE(MAX_PATH))
- #pragma push_macro("MAX_PATH")
- // 對 MAX_PATH 進行新的定義
- // 即使之前沒有定義 MAX_PATH,#undef MAX_PATH 也不會報錯
- #undef MAX_PATH
- #define MAX_PATH 512
- #pragma message("MAX_PATH: " _STRINGIZE(MAX_PATH))
- // 使用新的 MAX_PATH
- // 恢復 windef.h 的 MAX_PATH 定義
- #pragma pop_macro("MAX_PATH")
- #pragma message("MAX_PATH: " _STRINGIZE(MAX_PATH))
#pragma message 輸出如下:
- MAX_PATH: 260
- MAX_PATH: 512
- MAX_PATH: 260
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma warning 禁用和啟用編譯警告^
例子:
- // 禁用 C4507、C4034 警告(VC 的 warning 編號從 C4001 開始)
- // 只報告一次 C4385 警告
- // 將 C4164 警告作為編譯錯誤
- #pragma warning(disable: 4507 34; once: 4385; error: 164)
- #pragma warning(push) // 保存當前的警告設置:全局警告級別和 disable 的
- #pragma warning(disable: 4705) // 禁用某些警告
- #pragma warning(disable: 4706)
- #pragma warning(disable: 4707)
- // 會產生 C4705、C4706、C4707 警告的代碼
- #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 如下:
- // DllProj.h
- #ifndef _DLLPROJ_H_
- #define _DLLPROJ_H_
- #ifdef DLLPROJ_EXPORTS
- #define DLLPROJ_API __declspec(dllexport)
- #else
- #pragma comment(lib, "DllProj.lib") // 指示在導入符號時鏈接 DllProj.lib
- #define DLLPROJ_API __declspec(dllimport)
- #endif
- // 省略代碼
- #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") 設置可讀寫、共享區段
- #pragma comment(linker, "/SECTION:.mydata,RWS")
- #pragma data_seg(".mydata")
- // .mydata 區段中的變量定義
- #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 創建可讀寫、共享區段
- #pragma section(".mydata", read, write, shared) // 在 .obj 中新建可讀寫、共享區段 .mydata
- // #pragma comment(linker, "/SECTION:.mydata,RWS") // 作用與上類似:在鏈接時調整區段屬性
- #pragma data_seg(".mydata") // 將以下初始化數據放置到 .mydata
- // .mydata 區段中的變量定義
- #pragma data_seg() // 恢復默認的初始化數據區段 .data
- __declspec(allocate(".mydata")) // 用 __declspec(allocate) 放置數據到區段
- 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 鏈接約定函數
- extern "C" void TestFunc_1(); // 必需在 .mycode 之前有函數聲明
- extern "C" void TestFunc_2(); // 並且是 C 鏈接約定的
- #pragma section(".mycode", read, execute, nopage) // 建立可執行、非分頁區段
- // #pragma comment(linker, "/SECTION:.mycode,RE!P") // 作用與上類似:在鏈接時調整區段屬性
- #pragma alloc_text(".mycode", TestFunc_1, TestFunc_2) // 將指定函數放到 .mycode 中
- void TestFunc_1()
- {
- // TestFunc_1 函數體
- }
- void TestFunc_2()
- {
- // TestFunc_2 函數體
- }
" 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 放置函數
- #pragma section(".mycode", read, execute, nopage) // 建立可執行、非分頁區段
- // #pragma comment(linker, "/SECTION:.mycode,RE!P") // 作用與上類似:在鏈接時調整區段屬性
- #pragma code_seg(".mycode") // 將以下函數放到 .mycode 區段中
- void TestFunc_1()
- {
- // TestFunc_1 函數體
- }
- void TestFunc_2()
- {
- // TestFunc_2 函數體
- }
- #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 放置只讀常量數據
例子:自定義區段和數據性質沖突
以下錯誤編譯器不會報錯,但實際沒有放置到期望的區段中
- int g_Var1 = 1; // 放置到 .data 中
- int g_Var2 = 0; // 放置到 .obj 的 .bss 中,鏈接時合並到 .data
- int g_Var3; // 同上
- const int g_Var4 = 1; // 編碼到代碼中,沒有放置到 .rdata 中
- const char g_szVar5[]= "foo"; // 放置到 .rdata 中
- #pragma const_seg(".myrdata")
- const char g_szVar6[]= "foo"; // 放置到 .myrdata 中
- #pragma const_seg()
- #pragma bss_seg(".mybss")
- int g_Var7 = 1; // 錯誤:放置到 .data 中
- int g_Var8 = 0; // 錯誤:放置到 .obj 的 .bss 中,鏈接時合並到 .data
- int g_Var9; // 正確放置到 .mybss 中
- #pragma bss_seg()
- #pragma data_seg(".mydata")
- int g_Var10 = 0; // 正確放置到 .mydata 中
- int g_Var11; // 錯誤:放置到 .obj 的 .bss 中,鏈接時合並到 .data
- #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 設置成員字節對齊
- // x86 32bit
- #pragma pack(4) // 用一對 #pragma pack(4) | #pragma pack()
- // #pragma pack(push, 4) // 用一對 #pragma pack(push, 4) | #pragma pack(pop)
- struct TestStruct
- {
- double a; // sizeof(double) = 8
- int b; // sizeof(int) = 4
- }; // sizeof(TestStruct) = 12
- // #pragma pack(4) 會一直作用,直到改變 pack
- #pragma pack() // 恢復編譯選項 /Zp 設置的字節對齊
- // #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
- #pragma auto_inline(off)
- int simple_func(int a) // 不會 inline 化
- inline // #pragma auto_inline(off) 不會作用於顯式指定的 inline 函數
- 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
- #pragma optimize("pt", on) // 對下面的代碼使用 fp:precise, /Ot 優化
- // 函數定義
- #pragma optimize("", off) // 關閉上次 #pragma optimize 指定的優化
- #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
- #include <string.h>
- // 使用 /Oi 編譯選項
- #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 警告
例子:
- // 使用 #pragma deprecated
- // BEGIN
- //
- #pragma deprecated(OldClass)
- class OldClass1;
- #pragma deprecated(test_func1)
- void old_func1();
- //
- // END
- // 使用 __declspec(deprecated)
- // BEGIN
- #define DEPRECATED_WILL_RMOVED "** will be removed in next version **"
- // deprecated() 中的字符串不是必需的,如果有,會在警告時輸出
- __declspec(deprecated(DEPRECATED_WILL_RMOVED)) void old_func2();
- // 注意 __declspec(deprecated) 修飾 class 時的位置
- class __declspec(deprecated) OldClass2;
- //
- // END
- void test()
- {
- old_func1(); // 產生 C4995 警告
- OldClass1 obj; // 產生 C4995 警告
- old_func2(); // 產生 C4996 警告,並輸出 "** will be removed in next version **"
- OldClass2(); // 產生 C4996 警告
- }
" 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: 折疊或展開光標所在的代碼塊
- #pragma region FuncTestCode // 折疊成一行后,(+) 后顯示的名字
- // 這里是一整塊代碼
- #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 定義頭文件別名^
- #pragma include_alias(<stdio.h>, <newstdio.h>)
- #pragma include_alias("api.h", "test\api.h")
- #include <stdio.h>
- #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:
- #include <stdio.h>
- int main()
- {
- #ifdef _DEBUG
- printf("Debug config\n");
- #else
- printf("Release config\n");
- #endif
- // MARK: TESTMACRO value
- printf("TESTMACRO: %d\n", TESTMACRO);
- return 0;
- }
預處理編譯命令:
cl /P /C /DTESTMACRO test.cpp
預處理輸出到 test.i:
- #line 1 "test.cpp"
- #line 1 "d:\\Visual Studio 8\\VC\\INCLUDE\\stdio.h"
- // #line 中 stdio.h 的路徑由實際 VC 安裝路徑而定
- // 這里省略 stdio.h 展開后的大量代碼
- #line 706 "d:\\Visual Studio 8\\VC\\INCLUDE\\stdio.h"
- #line 2 "test.cpp"
- int main()
- {
- printf("Release config\n");
- #line 10 "test.cpp"
- // MARK: TESTMACRO value
- printf("TESTMACRO: %d\n", 1);
- return 0;
- }
例子:過濾查看預處理展開結果^
用這種方法可以查看編譯過程中,實際的宏展開、預處理結果
以上面的 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 行
輸出結果如下:
- printf("Release config\n");
- // MARK: TESTMACRO value
- printf("TESTMACRO: %d\n", 1);
- return 0;
- }
" 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:
- test.cpp
- Note: including file: d:\Visual Studio 8\VC\INCLUDE\stdio.h
- Note: including file: d:\Visual Studio 8\VC\INCLUDE\crtdefs.h
- Note: including file: d:\Visual Studio 8\VC\INCLUDE\sal.h
- Note: including file: d:\Visual Studio 8\VC\INCLUDE\vadefs.h
- Note: including file: d:\Visual Studio 8\VC\INCLUDE\swprintf.inl