C语言宏的使用
使用条件宏进行条件编译
譬如,对于同一份代码,我想编译出两个不同的版本,在其中一个版本中去掉某一部分功能,
这时可以通过条件宏判断是否编译,例:
如果不使用条件宏进行控制,想编译两个不同版本的程序,就需要保存两份源代码。
条件编译的语法和if else语法类似,必须以#endif结尾例如:
#if 常量表达式 //代码1 #elif 常量表达式 //代码2 #elif 常量表达式 //代码3 #else //代码4 #endif
条件编译时将会根据常量表达式来求值,如果常量表达式的值为假,那么对应的代码将不会被 编译,还可以判断某个宏是否定义,例如:
#ifdef(NameOfMacro) //代码1 #else //代码2 #endif
如果NameOfMacro被定义,那么代码1将会参与编译,否则代码2参与编译,如下写法也是等价的:
#if defined(NameOfMacro) //代码1 #else //代码2 #endif
#if !defined(NameOfMacro) //代码2 #else //代码1 #endif
宏一般都定义在头文件中,要想使用改宏则使用#include包含该头文件即可,也可以定义在源文件中,但是这种情况比较少,一定要确保条件判断中的宏的定义位置位于条件判断之前,否则不会生效;宏的作用范围从定义它的位置开始到源文件结束,在其他源文件中该宏不可见,如果想使用某个宏,则必须使用#include指令包含对应的头文件。
除了在头文件和源文件中定义宏,还可以在定义预处理宏,
例如:在VS项目属性中
预处理宏只能是空的宏,但预处理宏作用于整个工程,对该工程中任意源文件可见,优先于任何定义在源码中的宏.
使用空宏进行说明
可以使用空宏对函数参数进行说明,微软的API中就使用了空宏,例如:
WINBASEAPI BOOL WINAPI WriteFileEx( _In_ HANDLE hFile, _In_reads_bytes_opt_(nNumberOfBytesToWrite) LPCVOID lpBuffer, _In_ DWORD nNumberOfBytesToWrite, _Inout_ LPOVERLAPPED lpOverlapped, _In_ LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );
In,_Inout_定义如下:
_In_表示该参数为传入参数,Inout_表改参数为传入传出参数,为了防止_In,_Inout_被
别人定了,所以先使用#ifdef判断该宏是否被定义,如果被定义则使用#undef取消定义,然后
在重新定义该宏。
#和##运算符
除了#define 标识符 记号序列 这种形式定义宏,还可以定义带有参数的宏,形式如下:
#define 标识符(标识符列表(可选)) 记号序列,
在定义带有形参的宏定义中,#和##将会影响宏替换的过程,如果记号序列中某一个参数前
面有一个#,那么替换时将会在这个参数前后插入",将其变成一个字符串,即使该参数是
另一个带参宏,也不会在进行宏展开替换;如果该参数为一个字符串常量,那么预处理器会
分别为字符串前后的"号进行转义,将"变成字符,然后在前后各插入一个";
例:
#define TEST0(p1) #p1"333" #define TEST(p1,p2,p3) #p1#p2 #p3 int main() { int nTest1; int nTest2; int nTest3; printf("%s", TEST(TEST0(0),"333",nTest3)); return 0; }
运行结果:
从上面的例子可以看出#运算符主要作用就是将宏定义中的参数变成字符串,而##运算符的
作用就是将宏定义中相邻的两个的参数连接在一起组成一个标识符,如果该标识符不符合C
语言表示符的组成规则,那么编译时会报错;
例如:
对于使用了##和#的宏定义,预处理器需要进行反复扫描分析记号序列,以此来查找已定义的
标识符进行替换,如果一个表示符在某次扫描中被替换后,再次扫描遇到此标识符时,则不在
进行替换。
例:
#define TEST(p1,p2) p1##p2 int main() { int nTest1; int nTest2; int nTest3; int TEST(TEST(nTest1, nTest2), nTest3) = 3; printf("%d", TEST(TEST(nTest1, nTest2), nTest3)); return 0; }
使用/P命令查看编译预处理后的结果:
int main() { int nTest1; int nTest2; int nTest3; int TEST(nTest1, nTest2)nTest3 = 3; printf("%d", TEST(nTest1, nTest2)nTest3); return 0; }
可以看出TEST(TEST(nTest1, nTest2), nTest3)第一次展开后为TEST(nTest1, nTest2)nTest3,
然后又遇到了TEST宏定义,则不在进行展开,直接将TEST(nTest1, nTest2)nTest3作为最终结果,但是
TEST(nTest1, nTest2)nTest3显然不符合C语言表示符的定义,所以编译时会报错。
注意:/P命令使用完成后,必须从命令行中删除它,否则编译的时候会报找不到xxxxx.obj错误
#和##运算符是C语言宏定义中两个比较有创造力的运算符,可以创造出许多"黑魔法"宏定义
预定义的宏
C语言中有些预定义的宏,这些宏不能取消定义和重定义: