1. 不能忽视宏定义中的空格
#define f (x) ((x)-1)
上面的宏定义中展开后变成
(x) ((x)-1)
而不是
((x)-1)
因为在f
和后面的(x)
之间多了一个空格!所以如果希望定义f(x)
为((x)-1)
,必须要这样写:
#define f(x) ((x)-1)
但调用宏的时候却可以有空格,即在上面完成宏定义后,f(3)
和f (3)
求值后都等于2.
2. 宏并不是函数
#define MAX(a,b) ((a)>(b)?(a):(b))
int max(int a, int b)
{
return (a>b?a:b);
}
在宏定义中,当一个操作数被两处用到时,就会被求值2次,但是函数不会!
int MAX_Val = 0,max_Val = 0;
int a=3,b=2;
MAX_Val = MAX(++a,b);
max_Val = max(++a,b);
运行结果MAX_Val
和max_Val
是不相等的,原因是因为宏展开时变成:
((++a)>(b)?(++a):(b))
在这里++a
运算了两次!
以前经常听到别人说用宏定义代替函数是“用空间换时间”,原因是在C语言中调用函数时造成的系统开销要大大多于函数体内的实际计算操作,所以用宏定义免去了调用函数的开销,自然时间就省了出来。但是宏展开可能产生非常庞大的表达式,占用的空间远远超过了编程者所期望的空间。例如:
#define max((a)>(b)?(a):(b))
假如用上面定义的宏max
,来找到a,b,c,d
四个数的最大者,首先想到的写法是:
max(a,max(b,max(c,d)))
展开后就是:
((a)>(((b)>(((c)>(d)?(c):(d)))?(b):(((c)>(d)?(c):(d)))))?(a):(((b)>(((c)>(d)?(c):(d)))?(b):(((c)>(d)?(c):(d))))))
这个式子太长了!如果调整下,写成:
max(max(a,b),max(a,b))
展开后
((((a)>(b)?(a):(b)))>(((c)>(d)?(c):(d)))?(((a)>(b)?(a):(b))):(((c)>(d)?(c):(d))))
还是比较长。
如果换成函数,更容易些!
int max(int a,int b,int c,int d)
{
int biggest = a;
if(biggest < b) biggest = b;
if(biggest < c) biggest = c;
if(biggest < d) biggest = d;
return biggest;
}
3. 宏并不是语句
#define assert(e) if(!(e)) assert_error(__FILE__,__LINE__)
宏assert
的这个定义,即使用在一个再明白直接不过的情形中,也会有一些难以察觉的错误。
if(x>0 && y>0)
assert(x>y);
else
assert(y>x);
展开后并做缩排处理:
if(x>0 && y>0)
if(!(x>y))
assert_error(__FILE__,__LINE__);
else
if(!(y>x))
assert_error(__FILE__,__LINE__);
这与我们所期望的流程结构不一样!
如果在宏assert
的定义中用大括号把宏体整个给“括”起来,那就没有这样的流程结构错误。但是依然有问题产生:
#define assert(e) \
{if(!(e)) assert_error(__FILE__,__LINE__);}
继续拿上面的例子,展开后变成
if(x>0 && y>0)
{if(!(x>y)) assert_error(__FILE__,__LINE__);};
else
{if(!(y>x)) assert_error(__FILE__,__LINE__);};
上面展开后在else
之前的分号是一个语法错误。要解决这个问题,一个办法是对assert
的调用后面都不要再跟一个分号,另外一个办法是:
#define assert(e) \
((void)((e)||assert_error(__FILE__,__LINE__)))