C语言中的宏是C编译器提供的预处理 命令的一种,在预处理期间(编译前 )进行求值替换。善用宏可以让代码准确(SPOT原则 )、高效(编译期求值 )、易维 护(容易理解 ),配合编译器本身提供的一些机制,可以在不破坏程序完整性的前提下,快捷方便的对程序执行调试(打开/关闭调试信息输出)。但是如果错误应 用或者滥用宏,也会导致晦涩的错误、让代码难以维护。本文对C语言中宏的应用做出详细的介绍,并就具体项目应用做分析,最后,提供项目级的宏调试代码文 件。
1. 宏的形式
分为两种形式。在一个宏定义中,宏的名称紧跟着一个左圆括弧,则称之为函数式宏 ;反之,则为对象式宏 。
(1) 对象式宏 :由宏名及可选的宏体构成。
#define name sequence-of-tokens(optional)
例如:
#define HTTP_REQUEST_SIZE 0x1024 /* http request packet size limitted */
#define MAX_CONCURRENT 5000 /* maximum value of server concurrent */
一般代码中大量存在相关宏的引用,当相关宏值需要更改时,只需要更改宏定义,而不用跟踪整个代码来替换。
(2) 函数式宏 :由宏名、紧接的左圆括弧及可选任意参数、右圆括弧及可选宏体构成。
#define name( identifier-list(optional) ) sequence-of-tokens(optional)
例如:
#define sum( x , y ) ( (x) + (y) )
#define tcp_socket() socket(PF_UNIX,SOCK_STREAM,0)
#define debug_printf(...) printf(__VA_ARGS__)
需要注意的是,左圆括弧必须紧接着宏名 ,否则,左圆括弧连同右边所有部分,会成为宏体,例如,编写如下代码段:
/* test01.c */
#define multiply ( x, y ) ( (x) * (y) )
int i = multiply(x,y);
使用 gcc 按照如下指令编译:
$ gcc -E macro.c
可以看到输出为:
# 1 "macro.c"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "macro.c"
int i = ( x, y ) ( (x) * (y) )(x,y);
而不是 我们期望的:
int i = ( (x) * (y) );
2. 宏的重新扫描
宏调用在扩展(替换)以后,会从替换宏体的开始处重新开始宏处理,以实现深层替换 。宏的所有替换不是发生在宏定义的地方,而是全部发生在宏调用的地方。例如,如下代码段:
#define plus(x,y) add(y,x)
#define add(x,y) ((x)+(y))
plus(plus(a,b),c)
其扩展过程分布描述如下:
Step Result
1. original plus(plus(a,b),c)
2. add(c, plus(a,b))
3. ((c)+(plus(a,b)))
4. ((c)+(add(b,a)))<