C语言之宏
所谓的宏就是一种预处理命令,什么是与处理呢?即在编译过程之前先对程序代码做出的必要的转换处理。宏有两个作用:
1.当遇到需要将程序某个特定的数量在程序中出现的所有实例通通加以修改时,程序只需改动一处即可。
2.大多数C语言函数调用时都会带来重大的系统开销,而宏看上去像一个函数却没有函数调用的开销。
宏虽然有优点,但是也有其自身的缺陷。
首先得明确宏只是简单地替换。例如
#define CHAR char*
Int main()
{
Char a=’A’;
CHAR p1,p2;
P1=&a;
P2=&a;//错误
}
在运行如上程序代码的时候程序会报错,什么原因呢?由于宏只是简单地替换,所以CHAR p1,p2;代表的是char *p1,p2;只有p1被定义成了指针,而p2只是字符类型的普通变量。
这样的简单替换往往会导致另一种错误出现:
#define add(a,b) a + b
result = add (1,2) * add(3,4);
如上的定义的本意是先计算1与2,3与4之和,然后把他们的结果乘起来。但是运行的结果却不是我们想象的那样,编译器会把表达式简单替换为:1+2*3+4,这样我们就可以看出问题的所在了。要解决这种问题的出现,就是不要吝啬括号的使用。应针对每个宏参数和整个表达式都加上括号,避免歧义的产生。
其次,不能忽略宏定义中的空格。例如:
#define f (x) ((x) * 2)
这种定义有两种可能的解释方式:
① 定义了宏 f, 其中f代表(x) ((x) * 2)
② 定义了宏 f (x),其中x是宏参数,f (x)宏整体代表((x) * 2)
这两种定义只能是第一种,因为在宏定义时中间不能有空格。有了空格首先会对宏的理解产生歧义性,其次在编译时会产生意想不到的结果。因此应该避免这种情况的发生。
以上的规则只适用于宏的定义,在宏调用的时候中间有没有空格都无所谓。即f(5)与
f (5)都是一样的。
最后,我们要关注的是宏的副作用。
宏看起来像函数,但它并不是函数。我们先看如下的代码:
#include<stdio.h>
#define max(a,b) a>b?a:b
int main()
{
char ar[3] = { 2, 3, 1 };
int i = 1; int biggest = ar[0];
while (i < 3)
{
biggest = max(biggest, ar[i++]);
}
printf("max value=%d\n", biggest);
return 0;
}
这段代码的本意是求出数组中最大的值,但是经过运行我们发现,运行的结果并不是我们所想象的3,而是1,这个结果明显错误,但是错在哪呢?我们先将上述的式子中的max进行宏替换:
biggest=biggest>ar[i++]?biggest:ar[i++];
首先,变量biggest与ar[i++]进行比较,此时i的值为1,ar[1]的值为3,而此时biggest的值为ar[0]为2,因此表达式的值为假。这里由于i++的副作用,比较后i递增为2.
因为关系运算的结果为假,所以ar[i++]的值被赋给biggest,然而此时i的值为2.所以实际赋给变量的值是ar[2]即1,这时,又因为i++的副作用,i的值变成3.因此也就不难理解为什么程序运行结果不是3而是1了。
要避免宏的副作用,这里有两个方法:
1.把i++提出来,即将原来的biggest=max(biggest,ar[i++]);
改为:biggest=max(biggest,ar[i]);
i++;
2.将宏改为函数