关键字:宏, 预定义宏, 预处理, 预编译头, VC, #pragma, 编译选项, 程序区段
C/C++ 预定义宏
- 例子:C/C++ 预定义宏的取值
- C/C++ 预定义宏用途:诊断与调试输出
- CRT 的诊断与调试输出:assert, _ASSERT/_ASSERTE, _RPTn/_RPTFn/_RPTWn/_RPTFWn
- MFC 的诊断与调试输出:ASSERT/VERIFY, ASSERT_VALID, TRACE/TRACEn
- MFC 的调试版 new
CRT 和 C 标准库中的宏
- NULL 空指针
- 例子:NULL 隐式转换和 0 是类型自动的
- limits.h 整数类型常量
- float.h 浮点类型常量
- 例子:浮点数极限值:判断浮点数是否相等
- math.h 数学常量
- EOF 常量
- 例子:标准输入的 EOF
- errno.h 错误代码
- locale 类别
- _MAX_PATH 等文件名与路径长度限制
- RAND_MAX 随机数最大值
- 例子:用 RAND_MAX 产生某个范围内的随机数
- va_arg/va_start/va_end 访问变长函数参数
- 宏实现的 CRT 函数
Microsoft 预定义宏
- 平台与系统类
- 版本号类
- 工程配置管理类
- 辅助类
- 例子:用 __VA_ARGS__ 打印跟踪函数调用
- 例子:用 __VA_ARGS__ 格式化 std::string
- 例子:用 __COUNTER__ 计数值定义掩码常量
- 例子:用 __FUNCTION__ 打印跟踪函数调用
Windows API 中的注释性宏
Windows API 中的常用宏
- 类型辅助类
- GDI 类
- 错误处理类
- 标记没有使用的参数、变量辅助宏
- 错误码、状态码
- 调用规范类
- 国际化类
- 资源类
- 网络类
字符串化操作符 #
- 用 # 操作构造字符串化宏 STRINGIZE
- CRT 中的 STRINGIZE 定义
- STRINGIZE 的展开规则
- 例子:用 STRINGIZE 查看宏的展开结果
拼接操作符 ##
- 例子:_T() 的定义
- 例子:Windows API 通用句柄类型的定义
- 例子:用 ## 构造函数名
TCHAR 统一字符类型和处理
- _TCHAR, _TEXT()/_T(), _t 系列函数
- TCHAR, LPTSTR/LPCTSTR, TEXT(), A/W 版本 Windows API
宏的缺点和替代方法
- 宏难于调试
- 宏的使用缺陷
- 宏造成全局名字空间污染
- 优先使用宏的情况
条件编译
- 例子:注释大量代码
- 例子:MFC 中的调试版代码示例
- 例子:DLL 工程导出符号
- 例子:用 #undef 解决 wxWidgets 自定义事件链接 BUG
预编译头文件
- 使用 PCH 的编译命令
- 例子:典型的 MFC 工程预编译头 stdafx.h 代码
常用预处理指令
- #error 产生人工编译错误
- #line 改变行号和源文件名
- # 空指令
#pragma 预处理指令
- #pragma once 只包含一次头文件
- #pragma message 编译时输出消息
- 例子:用 #pragma message 和 STRINGIZE 查看宏的展开结果
- #pragma push_macro/pop_macro 保存和恢复宏定义
- #pragma warning 禁用和启用编译警告
- #pragma comment 目标文件注释和编译选项传递
- 例子:用 #pragma comment(lib) 实现库的 auto-link
- #pragma comment(linker) 传递链接选项
- #pragma comment(linker, "/SECTION") 设置区段属性
- #pragma 区段操作
- #pragma section 在目标文件中创建区段
- #pragma alloc_text 将 C 链接约定的函数放置到区段
- #pragma code_seg 将函数放置到代码区段
- #pragma data_seg/bss_seg/const_seg 将数据放置到区段
- #pragma pack 设置成员字节对齐
- #pragma inline 函数设置
- #pragma auto_inline 禁用和启用 auto-inline
- #pragma inline_depth 设置函数调用的 inline 化深度
- #pragma inline_recursion 禁用和启用递归函数的 inline 化
- #pragma 优化指令
- #pragma optimize 禁用或启动特定优化
- #pragma intrinsic 使用 intrinsic 函数
- #pragma function 使用普通函数
- #pragma deprecated 声明废弃函数
- #pragma omp 使用 OpenMP 指令
- #pragma region/endregion 折叠代码块
- #pragma setlocale 设置源代码中字符串字面量的编码
- #pragma include_alias 定义头文件别名
预处理相关编译选项
- /D 定义宏
- 定义数字
- 定义字符串
- 空定义
- CL 环境变量使用 /D
- /E, /EP, /P 预处理选项
- 例子:预处理展开源文件
- 例子:过滤查看预处理展开结果
- /showIncludes 输出头文件列表
- 例子:查看 #include 头文件列表
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++ 中,上述编译方式下没有定义 __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 _CRTIMP void __cdecl _wassert(__in_z const wchar_t * _Message, __in_z const wchar_t *_File, __in unsigned _Line); 3 #define assert(_Expression) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) ) 4 5 // crtdbg.h 6 #define _ASSERT_EXPR(expr, msg) \ 7 (void) ((!!(expr)) || \ 8 (1 != _CrtDbgReportW(_CRT_ASSERT, _CRT_WIDE(__FILE__), __LINE__, NULL, msg)) || \ 9 (_CrtDbgBreak(), 0)) 10 11 #ifndef _ASSERT 12 #define _ASSERT(expr) _ASSERT_EXPR((expr), NULL) 13 #endif 14 15 #ifndef _ASSERTE 16 #define _ASSERTE(expr) _ASSERT_EXPR((expr), _CRT_WIDE(#expr)) 17 #endif
CRT 的调试输出宏 _RPTn()/_RPTFn(),n: 0 ~ 5
_RPTWn()/_RPTFWn() 是宽字符版
1 // crtdbg.h 2 #define _RPT_BASE(args) \ 3 (void) ((1 != _CrtDbgReport args) || \ 4 (_CrtDbgBreak(), 0)) 5 6 #define _RPTF0(rptno, msg) \ 7 _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 // atltrach 2 3 #ifndef ATLTRACE 4 #define ATLTRACE ATL::CTraceFileAndLineInfo(__FILE__, __LINE__) 5 #define ATLTRACE2 ATLTRACE 6 #endif 7 8 // afh 9 10 #include <atltrach> 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"), pIsNull() ? _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_equal0,0 + DBL_EPSILON)); // FALSE 32 TEST_BOOL_FUNC(double_equal0,0 - DBL_EPSILON)); // FALSE 33 TEST_BOOL_FUNC(double_equal0,0 + DBL_MIN)); // TRUE 34 TEST_BOOL_FUNC(double_equal0,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 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 2.// 定义宽字符流与控制台 I/O 字符之间的转换字符集编码为系统 ANSI 字符集 3 3.// 这样在中文 Windows 上可输入、显示中文字符 4 4._tsetlocale(LC_ALL, _T("")); 5 5. 6 6.// 要用存储空间 >= _gettchar() 返回值类型的变量保存其返回值 7 7.// 而不要用 char ch = _getchar(),那样会截断其返回值类型 8 8.int ch; 9 9.while ((ch = _gettchar()) != _TEOF) 10 10. _tprintf(_T("[%c]"), (_TCHAR)ch); 11 11. 12 12._tprintf(_T("\nread stdin: %s\n"), (feof(stdin) ? _T("EOF") : _T("Error")));
测试输出,用 Ctrl + Z 产生 EOF 信号:
1 abc汉字 2 2.[a][b][/c][汉][字][ 3 3.]^Z 4 4. 5 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 版本号
工程配置管理类^
_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__ 用法:
1 // 针对参数不为 void,且需要保存返回值的函数 2 #define CALL_TRACE(func, re.) { _tprintf(_T("call: %s\n"), _TSTRINGIZE(func)); ret = func(__VA_ARGS__); } 3 // 针对返回值为 void 或不关心返回值的函数 4 #define CALL_TRACE_VOID(fun.) { _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, s.) { sprintf_s(buf, sz, __VA_ARGS__); str = buf; } 7 #define FORMAT_WSTRING(str, buf, s.) { swprintf_s(buf, sz, __VA_ARGS__); str = buf; } 8 #define FORMAT_TSTRING(str, buf, s.) { _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 // WinBah 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 WinErh 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 // crtdh 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 // tch 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 // wih 2 typedef void *HANDLE; 3 #define DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name 4 5 // 因此多数 Windows 句柄是指向桩结构的指针,如 HWND: 6 7 // winh 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 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 时才会分配内存):
1 const char* DEF_FILENAME = "defatxt"; 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) 7
(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 // DllPh 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_Ch 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 11 // redefine_WXDLLIMPEXP_Ch 12 13 // 不要用 #pragma once 等包含一次技巧 14 15 #ifdef REMOVE_WXDLLIMPEXP_CORE 16 # undef WXDLLIMPEXP_CORE 17 // 以下块拷贝自 10 dlimph,用于恢复 WXDLLIMPEXP_CORE 的原有定义 18 // BEGIN 19 # ifdef WXMAKINGDLL_CORE 20 # define WXDLLIMPEXP_CORE WXEXPORT 21 # define WXDLLIMPEXP_DATA_CORE(type) WXEXPORT type 22 # elif defined(WXUSINGDLL) 23 # define WXDLLIMPEXP_CORE WXIMPORT 24 # define WXDLLIMPEXP_DATA_CORE(type) WXIMPORT type 25 # else /* not making nor using DLL */ 26 # define WXDLLIMPEXP_CORE 27 # define WXDLLIMPEXP_DATA_CORE(type) type 28 # endif 29 // END 30 # undef REMOVE_WXDLLIMPEXP_CORE 31 #endif 32 // 用户代码 33 34 // 定义 WXDLLIMPEXP_CORE 为空 35 #include <undefine_WXDLLIMPEXP_Ch> 36 37 // 自定义事件 38 BEGIN_DECLARE_EVENT_TYPES() 39 DECLARE_EVENT_TYPE(wxEVENT_TEST_TRIGGERED, wxID_ANY) 40 // DECLARE_LOCAL_EVENT_TYPE(wxEVENT_TEST_TRIGGERED, wxID_ANY) // 用这个不会有 BUG 41 END_DECLARE_EVENT_TYPES() 42 43 // 重定义 WXDLLIMPEXP_CORE 44 #include <redefine_WXDLLIMPEXP_Ch>
说明
(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 /// @file stdh 3 /// @brief Windows 标准预编译头文件 4 /// 5 /// 将标准库、运行时库、基本库、Windows API、第三方库的头文件在这里包含,生成 6 /// 预编译头文件 MFCBapch 7 /// 8 /// 如果不修改 stdh,增量编译时便不会重新编译 stdh 中包含的头文件,这样 9 /// 加快了编译速度 10 /// 11 /// @version <version> 12 /// @author <author> 13 /// @date 2011-07 14 /// 15 /// Copyright (c) 2011, <company> 16 /// All rights reser 17 /// 18 19 20 // 典型的“只包含一次”条件编译技巧 21 // VC cl 编译器版本 10 以上 (_MSC_VER > 1000) 也可以使用 #pragma once 指令 22 #ifndef _STDAFX_H_ 23 #define _STDAFX_H_ 24 25 // 排除很少使用的 Windows 头文件 26 27 #define WIN32_LEAN_AND_MEAN // 适用于 Windows API 28 29 #ifndef VC_EXTRALEAN 30 #define VC_EXTRALEAN // 适用于 MFC 31 #endif 32 33 // 指定目标系统和环境 (Windows, IE) 的版本号 34 35 #ifndef WINVER 36 #define WINVER 0x0501 // 目标系统具有 Windows XP 及以上特性 37 #endif 38 39 #ifndef _WIN32_WINNT 40 #define _WIN32_WINNT 0x0501 // 目标系统具有 Windows XP 及以上特性 41 #endif 42 43 #ifndef _WIN32_WINDOWS 44 #define _WIN32_WINDOWS 0x0410 // 目标系统具有 Windows 98 及以上特性 45 #endif 46 47 #ifndef _WIN32_IE 48 #define _WIN32_IE 0x0600 // 目标系统具有 I0 及以上特性 49 #endif 50 51 52 /// Include Header 53 54 55 // C 标准库与运行时库 (CRT) 56 // BEGIN 57 // 58 #define _CRT_SECURE_NO_DEPRECATE // 使用废弃 (deprecated) 的 CRT 函数时,不产生编译警告 59 #define _CRT_SECURE_NO_WARNINGS // 典型的废弃函数有不带缓冲区大小检查的 strcpy()、strcat()、sprintf() 等 60 61 #include <stdh> 62 #include <tch> 63 #include <crth> 64 #include <strh> 65 // 66 // END 67 68 // C++ 标准库 69 // BEGIN 70 // 71 #include <exception> 72 #include <typeinfo> 73 // 74 // END 75 76 // MFC 库 77 // BEGIN 78 // 79 #ifndef _SECURE_ATL 80 #define _SECURE_ATL 1 // ATL/MFC 的安全设置 81 #endif 82 83 #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // 使 ATL/MFC 的 CString 具有显式地构造函数 (explicit) 84 85 #define _AFX_ALL_WARNINGS // 打开 MFC 的所有警告,包括一般可以安全忽略的警告 86 87 #include <afxh> // MFC 核心和标准支持 88 #include <afxh> // MFC 扩展支持 89 90 #ifndef _AFX_NO_OLE_SUPPORT 91 #include <afxdth> // MFC 的 IE 4 通用控件支持 92 #endif 93 94 #ifndef _AFX_NO_AFXCMN_SUPPORT 95 #include <afxh> // MFC 的 Windows 通用控件支持 96 #endif 97 // 98 // END 99 100 // Windows API 101 // BEGIN 102 // 103 // #include <Windh> // 使用 MFC 库时不要包含 Windh,MFC 头文件中已包含 104 #include <Winsoh> 105 // 106 // END 107 108 // Windows 通用控件 ComCtdll 版0 的内嵌 manifest 109 // BEGIN 110 // 111 #ifdef _UNICODE 112 #if defined _M_IX86 113 #pragma comment(linker, "/manifestdependency:\"type='win32' name='MicrosWindCommon-Controls' version0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") 114 #elif defined _M_IA64 115 #pragma comment(linker, "/manifestdependency:\"type='win32' name='MicrosWindCommon-Controls' version0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") 116 #elif defined _M_X64 117 #pragma comment(linker, "/manifestdependency:\"type='win32' name='MicrosWindCommon-Controls' version0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") 118 #else 119 #pragma comment(linker, "/manifestdependency:\"type='win32' name='MicrosWindCommon-Controls' version0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 120 #endif 121 #endif // _UNICODE 122 // 123 // END 124 125 #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 // tcpp 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 // 实际文件名:testcpp 2 #line 1 "testcpp" 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 // th 2 #if _MSC_VER > 1000 3 #pragma once 4 #endif 5 6 // 头文件中的代码
它和传统的 #ifndef 只包含一次技巧的功能相同:
1 // th 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 // 保存来自 winh 的 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 // 恢复 winh 的 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 // DllPh 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, "DllPlib") // 指示在导入符号时链接 DllPlib 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, "/SECTImydata,RWS") 2 #pragma data_semydata") 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 sectiomydata", read, write, shared) //obj 中新建可读写、共享mydata 2 // #pragma comment(linker, "/SECTImydata,RWS") // 作用与上类似:在链接时调整区段属性 3 #pragma data_semydata") // 将以下初始化数据放mydata 4 5 mydata 区段中的变量定义 6 7 #pragma data_seg() // 恢复默认的初始化数据data 8 9 __declspec(allocatmydata")) // 用 __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 sectiomycode", read, execute, nopage) // 建立可执行、非分页区段 5 // #pragma comment(linker, "/SECTImycode,RE!P") // 作用与上类似:在链接时调整区段属性 6 #pragma alloc_texmycode", 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 sectiomycode", read, execute, nopage) // 建立可执行、非分页区段 2 // #pragma comment(linker, "/SECTImycode,RE!P") // 作用与上类似:在链接时调整区段属性 3 4 #pragma code_semycode") // 将以下函数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; // 放objbss 中,链接时合data 4 int g_Var3; // 同上 5 6 const int g_Var4 = 1; // 编码到代码中,没有放rdata 中 7 const char g_szVar5[]= "foo"; // 放rdata 中 8 9 #pragma const_semyrdata") 10 const char g_szVar6[]= "foo"; // 放myrdata 中 11 #pragma const_seg() 12 13 #pragma bss_semybss") 14 int g_Var7 = 1; // 错误:放data 中 15 int g_Var8 = 0; // 错误:放objbss 中,链接时合data 16 int g_Var9; // 正确放mybss 中 17 #pragma bss_seg() 18 19 #pragma data_semydata") 20 int g_Var10 = 0; // 正确放mydata 中 21 int g_Var11; // 错误:放objbss 中,链接时合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 <strh> 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(<sth>, <newsth>) 2 #pragma include_alias("h", "test\h") 3 4 #include <sth> 5 #include "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 <sth> 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 "tcpp" 2 #line 1 "d:\\Visual Studio 8\\VC\\INCLUDE\\sth" 3 4 // #line 中 sth 的路径由实际 VC 安装路径而定 5 // 这里省略 sth 展开后的大量代码 6 7 #line 706 "d:\\Visual Studio 8\\VC\\INCLUDE\\sth" 8 9 #line 2 "tcpp" 10 11 int main() 12 { 13 14 printf("Release config\n"); 15 #line 10 "tcpp" 16 17 // MARK: TESTMACRO value 18 printf("TESTMACRO: %d\n", 1); 19 return 0; 20 }例子:过滤查看预处理展开结果^
用这种方法可以查看编译过程中,实际的宏展开、预处理结果
以上面的 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 tcpp 2 Note: including file: d:\Visual Studio 8\VC\INCLUDE\sth 3 Note: including file: d:\Visual Studio 8\VC\INCLUDE\crtdh 4 Note: including file: d:\Visual Studio 8\VC\INCLUDE\h 5 Note: including file: d:\Visual Studio 8\VC\INCLUDE\vadh 6 Note: including file: d:\Visual Studio 8\VC\INCLUDE\swpriinl标签:__,C++,gif,Client,pragma,ifdef,Images,anchor From: https://www.cnblogs.com/xll-arx-rvt-su/p/18560875