语法错误
当使用参数调用宏时,会将参数替换为宏主体,并与其他输入文件一起检查结果,以进行更多的宏调用,可以将部分来自宏主体和部分自变量的宏调用组合在一起。例如,
#define twice(x) (2*(x))
#define call_with_1(x) x(1)
call_with_1 (twice)
//x=1
→ twice(1)
→ (2*(1))
宏定义不必带有括号,通过在宏主体中编写不平衡的开放括号,可以创建一个从宏主体内部开始但在宏主体外部结束的宏调用。例如,
#define strange(file) fprintf (file, "%s %d",
…
strange(stderr) p, 35)
→ fprintf (stderr, "%s %d", p, 35)
组合宏调用的功能可能会很有用,但是在宏主体中使用不平衡的开放括号只会造成混淆,应该避免。
运算符优先级问题
在大多数宏定义示例中,每次出现的宏参数名称都带有括号,并且另一对括号通常会包围整个宏定义,这是编写宏最好的方式。举个例子
#define ceil_div(x, y) (x + y - 1) / y
假定其用法如下:
a = ceil_div(b&c,sizeof(int));
拓展开是
a =(b&c + sizeof(int)-1)/ sizeof(int);
这没有达到我们的预期,C的运算符优先级规则使其等效于此,而我们想要的是:
a =(((b&c)+ sizeof(int)-1))/ sizeof(int);
如果我们将宏定义为
#define ceil_div(x,y)((x)+(y)-1)/(y)
可能导致另一种情况,sizeof ceil_div(1,2)是一个C表达式,可以计算ceil_div(1,2)类型的大小,它扩展为:
sizeof((1)+(2)-1)/(2)
这将采用整数的大小并将其除以2,而除法包含在内部的sizeof之外。所以整个宏定义的括号可防止此类问题。那么,下面是定义ceil_div的正确方法如下
#define ceil_div(x,y)((((x)+(y)-1)/(y))
吞噬分号
通常需要定义一个扩展为复合语句的宏。例如,考虑以下宏,该宏跨空格字符前进一个指针(参数p表示在何处查找):
#define SKIP_SPACES(p, limit)
{ char *lim = (limit);
while (p < lim) {
if (*p++ != ' ') {
p--; break; }}}
该宏定义必须是单个逻辑行,严格来说,该调用扩展为复合语句,这是一个完整的语句,不需要用分号结束。
但是,由于它看起来像函数调用,因此,如果可以像使用函数调用一样使用它,则可以最大程度地减少混乱,然后再写一个分号,就像在SKIP_SPACES(p,lim)中一样。
这可能会在else语句之前出问题,因为分号实际上是空语句。假设你写
if (*p != 0)
SKIP_SPACES (p, lim);
else …
在if条件和else条件之间存在两个语句(复合语句和null语句)使C代码无效。
怎么解决?我们可以使用do…while语句更改宏SKIP_SPACES的定义以解决此问题。方法如下:
#define SKIP_SPACES(p, limit)
do { char *lim = (limit);
while (p < lim) {
if (*p++ != ' ') {
p--; break; }}}
while (0)
SKIP_SPACES (p, lim);扩展为
do {…} while (0);
这是一个陈述,循环仅执行一次,而且大多数编译器不会为此生成任何额外的代码。