首页 > 编程语言 >VC++ 中的宏 (#define) 与预处理 (#if/#ifdef/#pragma) 的使用总结

VC++ 中的宏 (#define) 与预处理 (#if/#ifdef/#pragma) 的使用总结

时间:2024-11-21 15:30:43浏览次数:1  
标签:__ C++ gif Client pragma ifdef Images anchor

关键字:宏, 预定义宏, 预处理, 预编译头, 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 Constantserrno, _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

    相关文章

    • C++:类的继承
      在C++中,类的继承是一种面向对象编程(OOP)的重要特性,它允许一个类(子类或派生类)继承另一个类(基类或父类)的属性和方法。通过继承,可以重用现有的代码,减少重复代码,提高代码的可维护性和扩展性。1.基本概念基类(BaseClass或ParentClass):被继承的类。派生类(DerivedClass或Chi......
    • C++最佳情侣身高差 2024年9月c++一级 电子学会中小学生软件编程C++等级考试一级真题答
      目录C++最佳情侣身高差一、题目要求1、编程实现2、输入输出二、算法分析三、程序编写四、程序说明五、运行结果六、考点分析C++最佳情侣身高差2024年9月C++编程等级考试一级编程题一、题目要求1、编程实现专家通过多组情侣研究数据发现,最佳的情侣身高差遵循......
    • 初识C++
      目录1.C++发展历史2.C++在工作领域中的应用3.第一个C++程序3.1梦开始的地方:打印helloworld4.命名空间4.1namespace的价值4.2namespace的定义4.3命名空间的使用5.C++输入&输出6.缺省参数7.函数重载8.引用8.1引用的概念的定义8.2引用的特性8.3引......
    • 深入计算机语言之C++:STL之vector的认识和使用
      ......
    • Qt/C++离线地图的加载和交互/可以离线使用/百度和天地图离线/支持手机上运行
      一、前言说明在地图应用中,有很多时候是需要断网环境中离线使用的,一般会采用两种做法,一种是只下载好离线瓦片地图,然后根据不同的缩放和经纬度坐标绘制瓦片。这种方式优点是任何地图都支持,只需要拿到瓦片即可,缺点是其他的接口都需要自己处理,比如覆盖物的绘制,工作量巨大,尽管Qt的qml......
    • C++基础知识概述(二)
      一、形参带默认值的函数 二、inline内联函数三、const用法1.const修饰的变量不能作为左值,初始化完成后,值不能修改 2.C与C++中const的区别是:编译方式不同 C:作为一个变量来编译生成指令,C中的const可以不必被初始化,能正常编译,不叫常量,叫常变量。constinta=20;......
    • 深入理解C++11右值引用与移动语义:高效编程的基石
      文章目录前言......
    • C++ list (链表)容器
      C++ list 链表#include<iostream>usingnamespacestd;#include<list>voidprintList(constlist<int>&L){ for(list<int>::const_iteratorit=L.begin();it!=L.end();it++) { cout<<*it<<"";......
    • C++ 模板元编程高级技巧与大型项目架构中的应用实践
      C++模板元编程(TemplateMetaprogramming,TMP)是一种利用C++模板在编译时进行计算和逻辑推理的技术。模板元编程可以极大地提升程序的灵活性、性能和可扩展性,尤其是在大型项目的架构中,能够有效地处理类型推导、优化计算和代码生成等任务。随着C++11、C++14、C++17和C++20......
    • C++零基础入门:趣味学信息学奥赛从“Hello World”开始
      编程学习的第一步,往往从“HelloWorld”开始。这不仅是程序员的“入门仪式”,更是打开编程世界的一把钥匙。结合树莓派Pico开发板的实际操作,这篇文章将为C++零基础的学生和信息学奥赛爱好者讲解如何通过一个简单的“HelloWorld”项目,学会基础语法、编程思维,以及软硬件结合的实......