带参数的宏定义
不带参数的宏定义只能完成一些简单的替换数值操作,如果希望程序在完成替换的过程中,能够进行一些更加灵活的操作,例如,根据不同的半径计算圆的周长,这时可以使用带参数的宏定义。带参数的宏定义语法格式如下:
#define 标识符(参数1,参数2,…) 字符串
和不带参数的宏定义格式相比,带参数的宏定义语法格式中多了一个包含若干参数的括号,括号中的多个参数之间使用逗号分隔。对于带参数的宏定义来说,同样需要使用字符串替换宏名,使用实参替换形参。
接下来通过一个计算圆周长的案例演示带参数宏定义的使用,如例1所示。
例1 definePara.c
1 #include <stdio.h>
2 #define PI 3.14 //定义宏PI
3 #define COMP_CIR(x) 2 * PI * x //定义带参数的宏COMP_CIR
4 int main()
5 {
6 double r = 1.0;
7 printf("2 * pi * r = %f\n", COMP_CIR(r)); //引用宏COMP_CIR
8 return 0;
9 }
例1运行结果如图1所示。
图1 例1运行结果
在例1中,第2行代码定义了不带参数的宏PI;第3行代码定义了带参数x的宏COMP_CIR,用于表示半径为x的圆的周长;第6行代码定义了一个double类型的变量r,其值为1.0;第7行代码引用宏COMP_CIR,并将变量r作为其参数,最终输出半径为r的圆的周长。由图1可知,半径为1.0的圆的周长为6.28。
在例1中,第7行代码在预处理时,由于宏定义COMP_CIR(x)嵌套了宏定义PI,程序首先会将宏定义PI替换成3.14,然后将参数x替换成半径r,替换后的代码如下所示:
printf("2 * pi * r = %f\n", 2*3.14*r);
通过上面的例子,可能会让有的读者觉得,带参宏定义和带参函数有时可以实现同样的功能,但两者却有着本质上的不同,具体如表1所示。
表1 带参数的宏定义与带参数的函数的区别
基本操作 | 带参数的宏定义 | 带参数的函数 |
---|---|---|
处理时间 | 预处理 | 编译、运行时 |
参数类型 | 无 | 需定义参数类型 |
参数传递 | 不分配内存,无值传递的问题 | 分配内存,将实参值带入形参 |
运行速度 | 快 | 相对较慢,因为函数的调用会涉及到内存分配、参数传递、压栈、出栈等操作 |
带参宏定义非常灵活,而且宏定义在程序预处理的时候就执行了,相比于函数,宏定义的“开销”要小一些,因此很多程序员喜欢使用宏定义代替一些函数的功能。
但是,在使用带参数的宏定义时,务必要注意参数替换问题,例如,定义带参宏定义ABS,用于计算参数的绝对值,示例代码如下所示:
#define ABS(x) ((x) >= 0 ? (x) : -(x)) //定义宏计算x的绝对值
上面代码定义了带参数的宏ABS(x),用于计算参数x的绝对值,宏体是一个条件表达式,如果参数x>0,就返回x本身作为其绝对值,如果x>0不成立,就取-x作为其绝对值。
该宏定义本身没有任何问题,例如定义double x = 12;传入参数x,计算结果为12。但是,当传入++x时,计算结果会出现错误,具体如例2所示。
例2 abs.c
1 #include <stdio.h>
2 #define ABS(x) ((x) >= 0 ? (x) : -(x)) //定义宏计算x的绝对值
3 double compAbs(double x) //定义函数计算x的绝对值
4 {
5 return x >= 0 ? x : -x;
6 }
7 int main()
8 {
9 double x = 12, y = 12;
10 printf("ABS(++x) = %f\n", ABS(++x)); //宏ABS计算++x的绝对值
11 printf("compAbs(++y) = %f\n", compAbs(++y));//compAbs()计算++y绝对值
12 return 0;
13 }
例2运行结果如图2所示。
图2 例2运行结果
在例2中,第2行代码定义宏ABS;第3~6行代码定义compAbs()函数;宏定义ABS和函数comAbs()都用于计算绝对值。第10~11行代码,分别调用宏ABS和compAbs()函数计算++x和++y的绝对值。从图2中可以看出,两者计算出的结果不一样。显然compAbs()函数计算出的结果13才是正确的。这是因为宏ABS在替换时,首先会将参数进行替换,替换后的表达式如下所示:
((++x) >= 0 ? (++x) : -(++x)));
在上述代码中,当x等于12时,首先会被自增为13,判断13>=0成立,则取(++x)作为整个表达的结果,再次执行++x操作,x的结果变为14,将14返回作为整个表达式的结果,造成计算结果错误。因此,在使用宏定义代替函数时,一定要注意这样的参数替换问题,避免程序出现错误。