- C/C++ 预定义宏^
- CRT 和 C 标准库中的宏^
- Microsoft 预定义宏^
- Windows API 中的注释性宏^
- Windows API 中的常用宏^
- 字符串化操作符 #^
- 拼接操作符 ##^
- TCHAR 统一字符类型和处理^
- 宏的缺点和替代方法^
- 条件编译^
- 预编译头文件^
- 常用预处理指令^
- #pragma 预处理指令^
- #pragma once 只包含一次头文件^
- #pragma message 编译时输出消息^
- #pragma push_macro/pop_macro 保存和恢复宏定义^
- #pragma warning 禁用和启用编译警告^
- #pragma comment 目标文件注释和编译选项传递^
- #pragma 区段操作^
- #pragma pack 设置成员字节对齐^
- #pragma inline 函数设置^
- #pragma 优化指令^
- #pragma deprecated 声明废弃函数^
- #pragma omp 使用 OpenMP 指令^
- #pragma region/endregion 折叠代码块^
- #pragma setlocale 设置源代码中字符串字面量的编码^
- #pragma include_alias 定义头文件别名^
- 预处理相关编译选项^
作者:Breaker Zhao
转载请注明作者和原文链接
VC 2005 中的宏 (#define) 与预处理 (#if/#ifdef/#pragma) 的使用方法总结。
关键字:宏, 预定义宏, 预处理, 预编译头, VC, #pragma, 编译选项, 程序区段
- C/C++ 预定义宏
- CRT 和 C 标准库中的宏
- Microsoft 预定义宏
- Windows API 中的注释性宏
- Windows API 中的常用宏
- 字符串化操作符 #
- 拼接操作符 ##
- TCHAR 统一字符类型和处理
- 宏的缺点和替代方法
- 条件编译
- 预编译头文件
- 常用预处理指令
- #pragma 预处理指令
- #pragma once 只包含一次头文件
- #pragma message 编译时输出消息
- #pragma push_macro/pop_macro 保存和恢复宏定义
- #pragma warning 禁用和启用编译警告
- #pragma comment 目标文件注释和编译选项传递
- #pragma 区段操作
- #pragma pack 设置成员字节对齐
- #pragma inline 函数设置
- #pragma 优化指令
- #pragma deprecated 声明废弃函数
- #pragma omp 使用 OpenMP 指令
- #pragma region/endregion 折叠代码块
- #pragma setlocale 设置源代码中字符串字面量的编码
- #pragma include_alias 定义头文件别名
- 预处理相关编译选项
VC 中的宏使用方法参考 MSDN: Macros (C/C++)
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">C/C++ 预定义宏^
__LINE__: 当前源文件的行号,整数
__FILE__: 当前源文件名,char 字符串,使用 /FC 选项产生全路径
__DATE__: 当前编译日期,char 字符串,格式 Aug 28 2011
__TIME__: 当前编译时间,char 字符串,格式 06:43:59
__STDC__: 整数 1,表示兼容 ANSI/ISO C 标准,配合 #if 使用
__TIMESTAMP__: 最后一次修改当前文件的时间戳,char 字符串,格式 Sun Aug 28 06:43:57 2011
__cplusplus: 以 C++ 方式而非 C 语言方式编译时定义,VC 2005 中定义为 199711L,配合 #ifdef 使用
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:C/C++ 预定义宏的取值^
- // assert.h
- _CRTIMP void __cdecl _wassert(__in_z const wchar_t * _Message, __in_z const wchar_t *_File, __in unsigned _Line);
- #define assert(_Expression) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) )
- // crtdbg.h
- #define _ASSERT_EXPR(expr, msg) \
- (void) ((!!(expr)) || \
- (1 != _CrtDbgReportW(_CRT_ASSERT, _CRT_WIDE(__FILE__), __LINE__, NULL, msg)) || \
- (_CrtDbgBreak(), 0))
- #ifndef _ASSERT
- #define _ASSERT(expr) _ASSERT_EXPR((expr), NULL)
- #endif
- #ifndef _ASSERTE
- #define _ASSERTE(expr) _ASSERT_EXPR((expr), _CRT_WIDE(#expr))
- #endif
CRT 的调试输出宏 _RPTn()/_RPTFn(),n: 0 ~ 5
_RPTWn()/_RPTFWn() 是宽字符版
- // afx.h
- #define ASSERT(f) DEBUG_ONLY((void) ((f) || !::AfxAssertFailedLine(THIS_FILE, __LINE__) || (AfxDebugBreak(), 0)))
- #define ASSERT_VALID(pOb) DEBUG_ONLY((::AfxAssertValidObject(pOb, THIS_FILE, __LINE__)))
MFC 的调试输出宏 TRACE()/TRACEn(),n: 0 ~ 3
- // afx.h
- void* AFX_CDECL operator new(size_t nSize, LPCSTR lpszFileName, int nLine);
- #define DEBUG_NEW new(THIS_FILE, __LINE__)
- // 用户代码
- // 调试版 new
- #ifdef _DEBUG
- #define new DEBUG_NEW
- #endif
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">CRT 和 C 标准库中的宏^
VC CRT 和 C 标准库中的宏参考 MSDN: Global Constants
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">NULL 空指针^
NULL 在 stddef.h, stdio.h, stdlib.h 等多个头文件中定义,是地址/指针类型的 0,如下:
- // baby pointer wrapper
- class Pointer
- {
- public:
- // 非 explicit 构造函数,说明 Pointer 可以从指针类型 void* 隐式转换
- Pointer(void* p) : m_Ptr(p)
- {}
- bool IsNull() const
- {
- return (m_Ptr == NULL);
- }
- private:
- void* m_Ptr;
- };
- // 形参可以从指针类型 void* 隐式转换
- void TestPointer(Pointer ptr)
- {
- _tprintf(_T("ptr is %sNULL\n"), ptr.IsNull() ? _T("") : _T("NOT "));
- }
- // 用户代码
- TestPointer(0); // OK,0 是类型自动的,0 被自动转换为 void*,再次隐式转换为 Pointer
- TestPointer(NULL); // OK,NULL 就是 0,同上
- TestPointer(1); // Error,C++ 中 1 不同于 0,它是确定的 int 类型,
- // 只能提升转换到 float/double 类型,不能自动转换为指针
- TestPointer((int*)1); // OK,强制转换 1 为 int*,int* 自动转换为 void*,再次隐式转换为 Pointer
- // 注意:void* 到 int* 不能自动转换,需要强制,参考 malloc() 的返回值
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">limits.h 整数类型常量^
在 limits.h 中定义,定义了各种 int 类型 (unsigned, char, short, long, __int64) 的最小、最大值,如 SCHAR_MAX (signed char MAX)、UCHAR_MAX (unsigned char MAX)、USHRT_MAX (unsigned short MAX) 等。编译时,如果 int 字面量超出这些范围,会编译出错
参考 MSDN: Integer Limits
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">float.h 浮点类型常量^
在 float.h 中定义,定义各种浮点类型 (float, double, long double) 的极限值,如最小、最大值,最小浮点差量 (epsilon) 等
参考 MSDN: Floating Limits
例子:浮点数极限值:判断浮点数是否相等^
- #define _USE_MATH_DEFINES
- #include <math.h>
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">EOF 常量^
EOF (end-of-file) 常量,定义为 (-1),有宽字符版 WEOF ((wint_t)(0xFFFF)),EOF 和 WEOF 在 stdio.h 中定义,还有 _TCHAR 版 _TEOF,在 tchar.h 中定义。EOF 在流、I/O 操作中表示到达流、文件末尾(EOF 条件),也用来表示发生错误情况
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:标准输入的 EOF^
- abc汉字
- [a][b][/c][汉][字][
- ]^Z
- read stdin: EOF
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">errno.h 错误代码^
在 errno.h 中定义,是测试全局量 errno 的值,errno 在 VC 中实现为线程安全的函数,而非全局变量。错误代码以 E 打头如 EINVAL:不合法的参数错误
错误代码具体值参考 MSDN: errno Constants 和 errno, _doserrno, _sys_errlist, and _sys_nerr
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">locale 类别^
locale 类别 (Categories),在 locale.h 中定义,如 LC_ALL、LC_CTYPE
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">_MAX_PATH 等文件名与路径长度限制^
包括全路径与各部分路径的限制,即 FILENAME_MAX、_MAX_PATH、_MAX_DRIVE、_MAX_EXT、_MAX_FNAME、_MAX_DIR,在 stdlib.h 中定义。最大全路径长度限制在 260,和 Windows 的 MAX_PATH 相同,这是为了兼容 Windows 98 FAT32 文件系统。CRT 支持 32767 长度的文件名,方法和 Windows API 相同,即使用 "\\?\" 路径前缀,并调用 Unicode 宽字符版的 CRT 函数
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">RAND_MAX 随机数最大值^
在 stdlib.h 中定义为 32767,rand() 函数会产生 0 ~ RAND_MAX 之间的伪随机 int 值
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:用 RAND_MAX 产生某个范围内的随机数^
- // 针对参数不为 void,且需要保存返回值的函数
- #define CALL_TRACE(func, ret, ...) { _tprintf(_T("call: %s\n"), _TSTRINGIZE(func)); ret = func(__VA_ARGS__); }
- // 针对返回值为 void 或不关心返回值的函数
- #define CALL_TRACE_VOID(func, ...) { _tprintf(_T("call: %s\n"), _TSTRINGIZE(func)); func(__VA_ARGS__); }
- // 针对参数为 void 的函数
- // NOTE: 函数 func() 使用 func(__VA_ARGS__) 展开时,会影响前面的变长参数函数 _tprintf(),
- // 导致运行时缓冲区访问违例(Debug 方式产生保护中断),所以不能用前两版带 func(__VA_ARGS__) 的 CALL_TRACE
- #define CALL_TRACE_VOIDPARM(func, ret) { _tprintf(_T("call: %s\n"), _TSTRINGIZE(func)); ret = func(); }
- // 针对返回值、参数均为 void 的函数
- #define CALL_TRACE_VOID_VOIDPARM(func) { _tprintf(_T("call: %s\n"), _TSTRINGIZE(func)); func(); }
- // 用户代码
- // Unicode 方式编译时,输出 call: CreateFileW,并将返回值传给 hFile
- CALL_TRACE_RET(CreateFile, hFile, _T("bbb"), 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:用 __VA_ARGS__ 格式化 std::string^
- // 保证 MAKE_MASK 在所有其它使用 __COUNTER__ 代码之前,这样才能
- // 保证第一次 MAKE_MASK 时,产生 2 << 0
- #define MAKE_MASK0(maskname) maskname = 1
- #define MAKE_MASK(maskname) maskname = (2 << __COUNTER__) // 说明 __COUNTER__ 是支持嵌套展开的
- // 用户代码
- enum MyMask
- {
- MAKE_MASK0(MASK_0), // 2^0: 1
- MAKE_MASK(MASK_1), // 2^1: 2 << 0
- MAKE_MASK(MASK_2), // 2^2: 2 << 1
- MAKE_MASK(MASK_3), // 2^3: 2 << 2
- MAKE_MASK(MASK_4) // 2^4: 2 << 3
- // 最大 MASK = MASK_31 2^31: 2 << 30
- };
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:用 __FUNCTION__ 打印跟踪函数调用^
- // WinBase.h
- WINBASEAPI
- __out
- HANDLE
- WINAPI
- CreateFileW(
- __in LPCWSTR lpFileName,
- __in DWORD dwDesiredAccess,
- __in DWORD dwShareMode,
- __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
- __in DWORD dwCreationDisposition,
- __in DWORD dwFlagsAndAttributes,
- __in_opt HANDLE hTemplateFile
- );
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">Windows API 中的常用宏^
Windows API 包含大量旗标、掩码、状态码、错误码等常量式宏
函数式宏最常用的有:
类型辅助类^
- DWORD MAKEROP4(DWORD fore, DWORD back): used in MaskBlt()
- LONG DIBINDEX(WORD wColorTableIndex)
- COLORREF PALETTEINDEX(WORD wPaletteIndex)
- COLORREF PALETTERGB(BYTE bRed, BYTE bGreen, BYTE bBlue)
- COLORREF RGB(BYTE byRed, BYTE byGreen, BYTE byBlue)
- BYTE GetBValue(DWORD rgb)
- BYTE GetGValue(DWORD rgb)
- BYTE GetRValue(DWORD rgb)
- POINTS MAKEPOINTS(DWORD dwValue)
另外,BITMAP_WIDTHBYTES(bits) 不在 Windows API 中,但比较常用于位图:
- MAKE_HRESULT(sev, fac, code): 将 severity、facility、code 合并为 HRESULT
- HRESULT_CODE(hr): 取得 HRESULT 的 code 部分
- HRESULT_FACILITY(hr): 取得 HRESULT 的 facility 部分
- HRESULT_SEVERITY(hr): 取得 HRESULT 的 severity 位
- HRESULT_FROM_NT(nt_stat): 从 NTSTATUS 变换到 HRESULT
- HRESULT_FROM_WIN32(win_err): 从 Win32 状态码变换到 HRESULT
- SUCCEEDED(hr): HRESULT 是否表示成功
- FAILED(hr): HRESULT 是否表示失败
- IS_ERROR(hr): HRESULT 是否表示一个错误
Win32 状态码没有类似 MAKE_HRESULT 的宏,自定义 Win32 状态码时可以用 mc (Message Compiler) 工具处理 .mc 脚本,自动生成含自定义 Win32 状态码的头文件,同时生成用于 FormatMessage() 的状态码文本描述,参考 MSDN:Message Compiler
也可以自定义用于 Win32 状态码的 MAKE_WINERR():
- #define CALLBACK __stdcall
- #define WINAPI __stdcall
- #define WINAPIV __cdecl
- #define APIENTRY WINAPI
- #define APIPRIVATE __stdcall
- #define PASCAL __stdcall
COM 常用的调用规范辅助宏:
- WORD LANGIDFROMLCID(LCID lcid)
- WORD MAKELANGID(USHORT primaryLang, USHORT subLang)
- DWORD MAKELCID(WORD langID, WORD sortID)
- DWORD MAKESORTLCID(WORD langID, WORD sortID, WORD sortVersion)
- WORD PRIMARYLANGID(WORD lgid)
- WORD SORTIDFROMLCID(LCID lcid)
- WORD SORTVERSIONFROMLCID(LCID lcid)
- WORD SUBLANGID(WORD lgid)
资源类^
- LPARAM MAKEIPADDRESS(BYTE b0, BYTE b1, BYTE b2, BYTE b3)
- BYTE FIRST_IPADDRESS(LPARAM lParam)
- BYTE SECOND_IPADDRESS(LPARAM lParam)
- BYTE THIRD_IPADDRESS(LPARAM lParam)
- BYTE FOURTH_IPADDRESS(LPARAM lParam)
- LPARAM MAKEIPRANGE(BYTE low, BYTE high)
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">字符串化操作符 #^
将代码中某个名字转换为字符串字面量,即“加引号”,参考 MSDN: Stringizing Operator
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">用 # 操作构造字符串化宏 STRINGIZE^
- // crtdefs.h
- #ifndef _CRT_STRINGIZE
- #define __CRT_STRINGIZE(_Value) #_Value
- #define _CRT_STRINGIZE(_Value) __CRT_STRINGIZE(_Value)
- #endif
- #ifndef _CRT_WIDE
- #define __CRT_WIDE(_String) L ## _String
- #define _CRT_WIDE(_String) __CRT_WIDE(_String)
- #endif
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">STRINGIZE 的展开规则^
1. 如果 _STRINGIZE() 的参数是宏,那么宏代表的实际值也将被展开,即嵌套展开
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:用 STRINGIZE 查看宏的展开结果^
查看某个宏在当前编译配置 (Debug/Release, ANSI/Unicode) 下,实际表示的东西,如某个 _t 系列函数、Windows API 究竟表示哪个函数,可以利用 _STRINGIZE():
- Line: 24
- MAX_PATH: 260
- _DEBUG: 1, _UNICODE: 1
- _tprintf: wprintf
- CreateFile: CreateFileW
2. 如果 _STRINGIZE() 的参数单纯的变量、函数、类型、const、enum 常量,那么只是将 _STRINGIZE() 括号中的东西加引号而已,如下:
- int: int, val: val
- MUSIC_STATE: MUSIC_STATE, ST_STOP: ST_STOP
- ClassTest: ClassTest, obj: obj
- func: func
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">拼接操作符 ##^
将代码中两个名字拼接到一起,形成一个名字。## 操作“不加引号”,参考 MSDN: Token-Pasting Operator
- // tchar.h
- #ifdef _UNICODE
- #define __T(x) L ## x
- #else
- #define __T(x) x
- #define _T(x) __T(x)
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:Windows API 通用句柄类型的定义^
- // 音乐播放状态常量
- enum MUSIC_STATE
- {
- ST_STOP,
- ST_PLAY,
- ST_PAUSE,
- ST_BUTT
- };
- // 音乐播放状态结构
- // 里面有一个用于处理特定状态的回调函数 stat_proc
- typedef struct _MusicState
- {
- MUSIC_STATE stat;
- const _TCHAR* stat_name;
- int (*stat_proc)(void*);
- } MusicState;
- // 处理特定音乐播放状态的函数
- // 函数名的统一形式 proc_ ## stat,stat 是状态常量的名字
- int proc_ST_STOP(void*);
- int proc_ST_PLAY(void*);
- int proc_ST_PAUSE(void*);
- // 初始化音乐播放状态结构
- #define INIT_MUSIC_STATE(stat) {stat, _TSTRINGIZE(stat), proc_ ## stat}
- MusicState g_MusicState[ST_BUTT] =
- {
- INIT_MUSIC_STATE(ST_STOP),
- INIT_MUSIC_STATE(ST_PLAY),
- INIT_MUSIC_STATE(ST_PAUSE)
- };
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">TCHAR 统一字符类型和处理^
_TCHAR、_T()、_t 系列函数等东西叫做 Generic-Text Mapping,即使用宏进行统一字符类型编写,在不同的字符集编码工程配置 ANSI/UNICODE/MBCS 下替换为不同的实际函数或类型,参考 MSDN:Generic-Text Mappings,Using Generic-Text Mappings, Using TCHAR.H Data Types with _MBCS
工程的字符集配置的宏定义:
ANSI (SBCS, ASCII): _UNICODE 和 _MBCS 均未定义,使用 char 单字节字符集编码
UNICODE: _UNICODE 定义,使用 wchar_t 宽字符集编码,VC 默认 wchar_t 2 字节
MBCS: _MBCS 定义,使用 char 变长字符集编码,一个字符占一个或多个 char
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">_TCHAR, _TEXT()/_T(), _t 系列函数^
根据 _UNICODE、_MBCS 的定义,调用 ANSI/UNICODE/MBCS 不同字符集版本的 CRT 函数,或产生字面量,多在 tchar.h 中声明。_t 字符操作函数参考 MSDN:String Manipulation (CRT)
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">TCHAR, LPTSTR/LPCTSTR, TEXT(), A/W 版本 Windows API^
根据 UNICODE 的定义,调用 ANSI/UNICODE 不同字符集版本的 Windows API,或产生字面量,多在 WinBase.h、Windows.h 中声明
不成文约定:带 _ 前缀的代码,通常对应 CRT,而不带 _ 前缀的东西,通常对应 Windows API。A/W 版本 API 都是接收字符串参数的函数,但并非所有接收字符串的 API 都有 A/W 两个版本,如 GetProcAddress() 只是 A 版本函数,因为 DLL 中的导出符号用 ASCII 英文足够了
宏的缺点和替代方法^
宏是预编译行为,所做的是名字替换,它的缺点和替代方法如下:
宏难于调试^
编译时,宏不会产生用于调试的名字符号。如 #define MAX_PATH 260,在调试时,无法找到其符号名 MAX_PATH,而只是 260 数字
常量式宏可以用 const 和 enum 代替,在调试中可以查看 const、enum 的符号名,并且 const、enum 和宏的运行时开销是相同的(有使用 const、enum 时才会分配内存):
- #define SWAP(v1, v2, tmp) \
- tmp = v1; \
- v1 = v2; \
- v2 = tmp;
- // 用户代码
- if (condition)
- SWAP(a, b, t); // 逻辑问题
- if (condition) {
- SWAP(a, b, t); // OK
- }
解决方法:定义宏时用 {} 或 do {} while(0) 包起来,如下:
- class TestClass1
- {
- private:
- int m_Val;
- // private 限制对宏 MACRO_DEF_VAL 不起作用
- #define MACRO_DEF_VAL 128
- public:
- static const int CONST_DEF_VAL = 128;
- enum { ENUM_DEF_VAL = 128 };
- };
- class TestClass2
- {
- private:
- int m_Val;
- // 产生 C4005 警告:MACRO_DEF_VAL 被重复定义
- #define MACRO_DEF_VAL 256
- public:
- static const int CONST_DEF_VAL = 256;
- enum { ENUM_DEF_VAL = 256 };
- };
- // 用户代码
- // 宏 MACRO_DEF_VAL 是全局的,不能写为 TestClass1::MACRO_DEF_VAL
- _tprintf(_T("TestClass1: %d, %d, %d\n"), MACRO_DEF_VAL, TestClass1::CONST_DEF_VAL, TestClass1::ENUM_DEF_VAL);
- _tprintf(_T("TestClass2: %d, %d, %d\n"), MACRO_DEF_VAL, TestClass2::CONST_DEF_VAL, TestClass2::ENUM_DEF_VAL);
输出结果:
后面定义的宏 MACRO_DEF_VAL 的值将前面的覆盖了:
- #if 0
- XXXXXXXXX
- #endif
- #if FALSE
- XXXXXXXXX
- #endif
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:MFC 中的调试版代码示例^
见上文“MFC 的调试版 new”
AssertValid() 参考 MSDN: MFC ASSERT_VALID and CObject::AssertValid
- // DllProj.h
- #ifndef _DLLPROJ_H_
- #define _DLLPROJ_H_
- #ifdef DLLPROJ_EXPORTS
- #define DLLPROJ_API __declspec(dllexport)
- #else
- #define DLLPROJ_API __declspec(dllimport)
- #endif
- #ifdef __cplusplus
- #define EXTERN_C extern "C"
- #define EXTERN_C_BEGIN extern "C" {
- #define EXTERN_C_END }
- #else // __cplusplus defined
- #define EXTERN_C extern
- #define EXTERN_C_BEGIN
- #define EXTERN_C_END
- #endif // __cplusplus NOT defined
- // 导出类
- class DLLPROJ_API TestClass
- {
- public:
- TestClass();
- };
- // 导出全局变量,以 C 的链接方式(修饰名、调用约定)
- EXTERN_C DLLPROJ_API int g_TestVal;
- // 导出函数
- DLLPROJ_API int TestFunc();
- #endif // _DLLPROJ_H_
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:用 #undef 解决 wxWidgets 自定义事件链接 BUG^
BUG 参考:wxEvent derived event,Custom Events
BUG 触发条件:以 DLL 方式使用 wxWidgets Windows 版本,即定义了 WXUSINGDLL,使用 DECLARE_EVENT_TYPE() 定义自定事件
BUG 表现:以 __declspec(dllimport) 修饰事件标识,实际上事件标识应该是一个模块内变量,而非导入变量,出现链接问题。MinGW GCC 4 报链接错误,VC 2005 报链接警告:warning C4273: inconsistent dll linkage。BUG 的具体原因请跟踪 wxWidgets 源码 event.h 中的 DECLARE_EVENT_TYPE() 和 dlimpexp.h 中的 WXDLLIMPEXP_CORE 定义
BUG 典型工程:开源下载器 MultiGet svn version 3
BUG 解决方法:
方法 1. 不使用旧的 DECLARE_EVENT_TYPE() 而使用 DECLARE_LOCAL_EVENT_TYPE() 定义自定事件
方法 2. 使用 DECLARE_EVENT_TYPE() 前后包含 undefine_WXDLLIMPEXP_CORE.h、redefine_WXDLLIMPEXP_CORE.h 头文件,以便取消和重定义 WXDLLIMPEXP_CORE
方法 2 中的 undefine_WXDLLIMPEXP_CORE.h、redefine_WXDLLIMPEXP_CORE.h 以及使用方法如下:
- // redefine_WXDLLIMPEXP_CORE.h
- // 不要用 #pragma once 等包含一次技巧
- #ifdef REMOVE_WXDLLIMPEXP_CORE
- # undef WXDLLIMPEXP_CORE
- // 以下块拷贝自 wx-2.8.10 dlimpexp.h,用于恢复 WXDLLIMPEXP_CORE 的原有定义
- // BEGIN
- # ifdef WXMAKINGDLL_CORE
- # define WXDLLIMPEXP_CORE WXEXPORT
- # define WXDLLIMPEXP_DATA_CORE(type) WXEXPORT type
- # elif defined(WXUSINGDLL)
- # define WXDLLIMPEXP_CORE WXIMPORT
- # define WXDLLIMPEXP_DATA_CORE(type) WXIMPORT type
- # else /* not making nor using DLL */
- # define WXDLLIMPEXP_CORE
- # define WXDLLIMPEXP_DATA_CORE(type) type
- # endif
- // END
- # undef REMOVE_WXDLLIMPEXP_CORE
- #endif
- ///
- /// @file stdafx.h
- /// @brief Windows 标准预编译头文件
- ///
- /// 将标准库、运行时库、基本库、Windows API、第三方库的头文件在这里包含,生成
- /// 预编译头文件 MFCBasic.pch
- ///
- /// 如果不修改 stdafx.h,增量编译时便不会重新编译 stdafx.h 中包含的头文件,这样
- /// 加快了编译速度
- ///
- /// @version <version>
- /// @author <author>
- /// @date 2011-07
- ///
- /// Copyright (c) 2011, <company>
- /// All rights reserved.
- ///
- // 典型的“只包含一次”条件编译技巧
- // VC cl 编译器版本 10 以上 (_MSC_VER > 1000) 也可以使用 #pragma once 指令
- #ifndef _STDAFX_H_
- #define _STDAFX_H_
- // 排除很少使用的 Windows 头文件
- #define WIN32_LEAN_AND_MEAN // 适用于 Windows API
- #ifndef VC_EXTRALEAN
- #define VC_EXTRALEAN // 适用于 MFC
- #endif
- // 指定目标系统和环境 (Windows, IE) 的版本号
- #ifndef WINVER
- #define WINVER 0x0501 // 目标系统具有 Windows XP 及以上特性
- #endif
- #ifndef _WIN32_WINNT
- #define _WIN32_WINNT 0x0501 // 目标系统具有 Windows XP 及以上特性
- #endif
- #ifndef _WIN32_WINDOWS
- #define _WIN32_WINDOWS 0x0410 // 目标系统具有 Windows 98 及以上特性
- #endif
- #ifndef _WIN32_IE
- #define _WIN32_IE 0x0600 // 目标系统具有 IE 6.0 及以上特性
- #endif
- /// Include Header
- // C 标准库与运行时库 (CRT)
- // BEGIN
- //
- #define _CRT_SECURE_NO_DEPRECATE // 使用废弃 (deprecated) 的 CRT 函数时,不产生编译警告
- #define _CRT_SECURE_NO_WARNINGS // 典型的废弃函数有不带缓冲区大小检查的 strcpy()、strcat()、sprintf() 等
- #include <stdlib.h>
- #include <tchar.h>
- #include <crtdbg.h>
- #include <string.h>
- //
- // END
- // C++ 标准库
- // BEGIN
- //
- #include <exception>
- #include <typeinfo>
- //
- // END
- // MFC 库
- // BEGIN
- //
- #ifndef _SECURE_ATL
- #define _SECURE_ATL 1 // ATL/MFC 的安全设置
- #endif
- #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // 使 ATL/MFC 的 CString 具有显式地构造函数 (explicit)
- #define _AFX_ALL_WARNINGS // 打开 MFC 的所有警告,包括一般可以安全忽略的警告
- #include <afxwin.h> // MFC 核心和标准支持
- #include <afxext.h> // MFC 扩展支持
- #ifndef _AFX_NO_OLE_SUPPORT
- #include <afxdtctl.h> // MFC 的 IE 4 通用控件支持
- #endif
- #ifndef _AFX_NO_AFXCMN_SUPPORT
- #include <afxcmn.h> // MFC 的 Windows 通用控件支持
- #endif
- //
- // END
- // Windows API
- // BEGIN
- //
- // #include <Windows.h> // 使用 MFC 库时不要包含 Windows.h,MFC 头文件中已包含
- #include <Winsock2.h>
- //
- // END
- // Windows 通用控件 ComCtl32.dll 版本 6.0 的内嵌 manifest
- // BEGIN
- //
- #ifdef _UNICODE
- #if defined _M_IX86
- #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
- #elif defined _M_IA64
- #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"")
- #elif defined _M_X64
- #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
- #else
- #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
- #endif
- #endif // _UNICODE
- //
- // END
- #endif // _STDAFX_H_
常用预处理指令^
VC 支持的预处理指令参考 MSDN: Preprocessor Directives
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#error 产生人工编译错误^
#error 产生 fatal error,编译器输出 #error 后的提示文本。指示该源文件必需使用 C++ 方式编译,如下:
- // 实际文件名:test_01.cpp
- #line 1 "test_02.cpp"
- _tprintf(_T("File: %s, Line: %d\n"), _T(__FILE__), __LINE__); // 实际第 200 行
输出:
File: test_02.cpp, Line: 1
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif"># 空指令^
没有作用的合法预处理指令行正则表达式:[\t ]*#[\t ]*
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma 预处理指令^
#pragma 是一组编译器特定的预处理指令,每种编译器的 #pragma 的子指令都有所不同。VC 的 #pragma 指令参考 MSDN: Pragma Directives and the __Pragma Keyword
常用的 #pragma 指令如下:
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma once 只包含一次头文件^
对头文件只包含一次,如下:
- // test.h
- #ifndef _TEST_H_
- #define _TEST_H_
- // 头文件中的代码
- #endif // _TEST_H_
在源文件中多次 #include 包含 test.h 时,不会出现 redefinition 错误
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma message 编译时输出消息^
#pragma message 在编译过程中,向标准输出或 VC 的 Output 窗口打印指定消息,作用:(1) 告知程序员代码编译和使用的注意事项 (2) 用于查看和诊断实际的编译代码
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">例子:用 #pragma message 和 STRINGIZE 查看宏的展开结果^
例 11 是用 STRINGIZE 在运行时输出宏的展开结果,其实在编译时也可以用 #pragma message 输出,诊断编译的实际代码:
- Line: 209
- MAX_PATH: 260
- _DEBUG: 1, _UNICODE: 1
- _tprintf: wprintf
- CreateFile: CreateFileW
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma push_macro/pop_macro 保存和恢复宏定义^
#pragma push_macro/pop_macro 用来解决宏命名冲突问题,如下:
- MAX_PATH: 260
- MAX_PATH: 512
- MAX_PATH: 260
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma warning 禁用和启用编译警告^
例子:
- // DllProj.h
- #ifndef _DLLPROJ_H_
- #define _DLLPROJ_H_
- #ifdef DLLPROJ_EXPORTS
- #define DLLPROJ_API __declspec(dllexport)
- #else
- #pragma comment(lib, "DllProj.lib") // 指示在导入符号时链接 DllProj.lib
- #define DLLPROJ_API __declspec(dllimport)
- #endif
- // 省略代码
- #endif // _DLLPROJ_H_
在使用工程中 #include 该文件,并设置库搜索路径 /LIBPATH,不必指定链接导入库 DllProj.lib
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma comment(linker) 传递链接选项^
#pragma comment(linker, "link_option")
link_option 只能为以下链接选项:
/DEFAULTLIB
/EXPORT
/INCLUDE
/MANIFESTDEPENDENCY
/MERGE
/SECTION
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma comment(linker, "/SECTION") 设置区段属性^
设置区段属性的方法有:
(1). 使用模块定义文件 .def 的 SECTIONS 定义,见 MSDN: SECTIONS (C/C++)
(2). 使用 /SECTION 链接选项,见 MSDN: /SECTION (Specify Section Attributes)
(3). 使用 #pragma section 指令创建区段
对于上面的方法 (2),可用 #pragma comment(linker) 指定链接选项
例子:#pragma comment(linker, "/SECTION") 设置可读写、共享区段
- #pragma section(".mydata", read, write, shared) // 在 .obj 中新建可读写、共享区段 .mydata
- // #pragma comment(linker, "/SECTION:.mydata,RWS") // 作用与上类似:在链接时调整区段属性
- #pragma data_seg(".mydata") // 将以下初始化数据放置到 .mydata
- // .mydata 区段中的变量定义
- #pragma data_seg() // 恢复默认的初始化数据区段 .data
- __declspec(allocate(".mydata")) // 用 __declspec(allocate) 放置数据到区段
- int g_Var = 0;
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma alloc_text 将 C 链接约定的函数放置到区段^
#pragma alloc_text 只能应用于 C 链接约定的函数,即对于 C++ 编译方式,需用 extern "C" 声明函数。所以 #pragma alloc_text 不支持重载函数和类成员函数
#pragma alloc_text 在函数声明与函数定义体之间使用
例子:在可执行、非分页区段中,用 #pragma alloc_text 放置 C 链接约定函数
- #pragma section(".mycode", read, execute, nopage) // 建立可执行、非分页区段
- // #pragma comment(linker, "/SECTION:.mycode,RE!P") // 作用与上类似:在链接时调整区段属性
- #pragma code_seg(".mycode") // 将以下函数放到 .mycode 区段中
- void TestFunc_1()
- {
- // TestFunc_1 函数体
- }
- void TestFunc_2()
- {
- // TestFunc_2 函数体
- }
- #pragma code_seg() // 恢复默认的标准代码区段 .text
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma data_seg/bss_seg/const_seg 将数据放置到区段^
数据性质和区段有对应关系,如果放置区段和数据性质冲突,则不会实际放到该区段中:
(1). .data 标准区段,放置初始化非 0 全局数据。用 #pragma data_seg 放置初始化数据,必需显示初始化其中变量(可以初始化为 0),否则不会放入 #pragma data_seg 指定的区段
(2). .bss 标准区段,放置未初始化、默认或显式初始化为 0 的全局数据。注意链接时 .bss 会向 .data 中合并,所以在 .exe/.dll 中看不到 .bss 区段,可查看 .obj 中的 .bss 区段。用 #pragma bss_seg 放置未初始化数据,必需不初始化其中变量(也不能初始化为 0),否则不会放入 #pragma bss_seg 指定的区段
(3). .rdata 标准区段,放置只读的全局常量数据。const 数字类型会编码到代码中(指令立即数),所以不放到 .rdata 中。用 #pragma const_seg 放置只读常量数据
例子:自定义区段和数据性质冲突
以下错误编译器不会报错,但实际没有放置到期望的区段中
- // x86 32bit
- #pragma pack(4) // 用一对 #pragma pack(4) | #pragma pack()
- // #pragma pack(push, 4) // 用一对 #pragma pack(push, 4) | #pragma pack(pop)
- struct TestStruct
- {
- double a; // sizeof(double) = 8
- int b; // sizeof(int) = 4
- }; // sizeof(TestStruct) = 12
- // #pragma pack(4) 会一直作用,直到改变 pack
- #pragma pack() // 恢复编译选项 /Zp 设置的字节对齐
- // #pragma pack(pop) // 恢复 #pragma pack(push, 4) 之前的字节对齐
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma inline 函数设置^
inline 函数修饰,参考 MSDN: inline, __inline, __forceinline
inline 函数编译优化选项,参考 MSDN: /Ob (Inline Function Expansion)
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma auto_inline 禁用和启用 auto-inline^
例子:
使用 /O2 编译优化选项,含 /Ob2:启动 auto-inline
- #pragma optimize("pt", on) // 对下面的代码使用 fp:precise, /Ot 优化
- // 函数定义
- #pragma optimize("", off) // 关闭上次 #pragma optimize 指定的优化
- #pragma optimize("", on) // 恢复到编译器 /O 选项指定的优化
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma intrinsic 使用 intrinsic 函数^
使用 intrinsic 函数编译选项 /Oi,参考 MSDN: /Oi (Generate Intrinsic Functions)
#pragma intrinsic,参考 MSDN: intrinsic
- // 使用 #pragma deprecated
- // BEGIN
- //
- #pragma deprecated(OldClass)
- class OldClass1;
- #pragma deprecated(test_func1)
- void old_func1();
- //
- // END
- // 使用 __declspec(deprecated)
- // BEGIN
- #define DEPRECATED_WILL_RMOVED "** will be removed in next version **"
- // deprecated() 中的字符串不是必需的,如果有,会在警告时输出
- __declspec(deprecated(DEPRECATED_WILL_RMOVED)) void old_func2();
- // 注意 __declspec(deprecated) 修饰 class 时的位置
- class __declspec(deprecated) OldClass2;
- //
- // END
- void test()
- {
- old_func1(); // 产生 C4995 警告
- OldClass1 obj; // 产生 C4995 警告
- old_func2(); // 产生 C4996 警告,并输出 "** will be removed in next version **"
- OldClass2(); // 产生 C4996 警告
- }
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma omp 使用 OpenMP 指令^
指令形式:
#pragma omp omp_directive
用于多线程、并发编程的 OpenMP 指令,子指令 omp_directive 参考 MSDN: OpenMP Directives
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">#pragma region/endregion 折叠代码块^
标记一整块代码,在 VC 编辑器可折叠成一行 (+) 和展开,见 VC 的 Edit->Outlining 菜单
VC Outlining 常用快捷键:
Ctrl + M, Ctrl + L: 折叠或展开所有的代码块
Ctrl + M, Ctrl + M: 折叠或展开光标所在的代码块
- #pragma include_alias(<stdio.h>, <newstdio.h>)
- #pragma include_alias("api.h", "test\api.h")
- #include <stdio.h>
- #include "api.h"
预处理相关编译选项^
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">/D 定义宏^
/D: 定义宏,作用类似 #define,但会去掉选项中的引号
/U: 取消指定的预定义宏,类似 #undef,如 /U _DEBUG
/u: 取消所有的预定义宏。/U 和 /u 都不能取消在源码中用 #define 定义的宏
定义数字^
- /DTESTMACRO: 等价 #define TESTMACRO 1,整数
- /DTESTMACRO=1: 同上
- /DTESTMACRO="1": 同上
- /DTESTMACRO=3.14: 等价 #define TESTMACRO 3.14,浮点数
- /DTESTMACRO="3.14": 同上
定义字符串^
- /DTESTMACRO="abcdef": 等价 #define TESTMACRO abcdef,非字符串字面量(没有引号)
- /DTESTMACRO=\"abcdef\": 等价 #define TESTMACRO "abcdef",字符串字面量
- /DTESTMACRO="\"abcdef\"": 同上
空定义^
- /DTESTMACRO=: 等价 #define TESTMACRO
- /DTESTMACRO="": 同上
- /DTESTMACRO=\"\": 等价 #define TESTMACRO "",非空定义,而是空字符串
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">CL 环境变量使用 /D^
SET CL=/DTESTMACRO#1: 用 # 代替 =,等价 /DTESTMACRO=1,即 #define TESTMACRO 1
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">/E, /EP, /P 预处理选项^
/E: 预处理源文件,结果输出到标准输出,去掉注释,在 #inlcude 展开和条件编译周围产生 #line 行号指示
/EP: 和 /E 相似,结果输出到标准输出,但不产生 #line
/P: 和 /E 相似,产生 #line,结果输出到文件 (test.cpp => test.i),相当于 cl /E test.cpp > test.i
/P /EP 联用: 结果输出到文件 test.i,且不产生 #line。/E、/EP、/P 不能和预编译头 PCH 联用
/C: 预处理时保留注释,和 /E、/P、/EP 联用
例子:预处理展开源文件^
源文件 test.cpp:
- #line 1 "test.cpp"
- #line 1 "d:\\Visual Studio 8\\VC\\INCLUDE\\stdio.h"
- // #line 中 stdio.h 的路径由实际 VC 安装路径而定
- // 这里省略 stdio.h 展开后的大量代码
- #line 706 "d:\\Visual Studio 8\\VC\\INCLUDE\\stdio.h"
- #line 2 "test.cpp"
- int main()
- {
- printf("Release config\n");
- #line 10 "test.cpp"
- // MARK: TESTMACRO value
- printf("TESTMACRO: %d\n", 1);
- return 0;
- }
例子:过滤查看预处理展开结果^
用这种方法可以查看编译过程中,实际的宏展开、预处理结果
以上面的 test.cpp 为例,预处理编译命令和 grep 过滤:
cl /EP /C /DTESTMACRO test.cpp 2>NUL | egrep -A 5 -B 5 "MARK: TESTMACRO"
2>NUL: 用于屏蔽输出 VC 编译器 banner 和提示、错误信息,用 /nologo 选项也可以
egrep -A 5 -B 5: 表示输出匹配正则表达式前后 5 行
输出结果如下:
- test.cpp
- Note: including file: d:\Visual Studio 8\VC\INCLUDE\stdio.h
- Note: including file: d:\Visual Studio 8\VC\INCLUDE\crtdefs.h
- Note: including file: d:\Visual Studio 8\VC\INCLUDE\sal.h
- Note: including file: d:\Visual Studio 8\VC\INCLUDE\vadefs.h