C语言允许定义带有参数的宏,其定义格式如下:
#define宏名(形参表)字符串
其中宏名是用户自定义标识符;宏定义中的参数仍可叫作形式参数;字符串中可以使用形参表中的形式参数。
例如:
#define SUM(a,b)a+b
则SUM就是一个带参数的宏,其形式参数为a和b,不同于函数定义中的形参,宏定义中的形参不需要定义类型。
带参数宏一般调用格式为:
宏名(实参表);
例如:
#define SUB(a,b) a-b //宏定义
在程序中可以写成以下调用:
x=SUB(5,3);
在编译预处理时,预处理器仍然对带参数的宏进行替换,其替换过程仍然是用“宏值”替换“宏名”,只是在替换过程中要用实参代替字符串中的形参,普通的字符仍旧原样替换即可。
如x=SUB(5,3)的替换结果为:
x=5-3;
替换时用实参5和3分别替换了字符串中的形参名字a和b,其中的“-”原封不动替换。
【例9-4】使用带参数宏求两个整数的最大值。
z=MAX(x,y)经预处理后会替换成x>y?x:y的形式,x的值是20,y的值是30,得出最大值是30是一个正常的结果。但此程序中包含了一些问题,如将程序修改如下:
程序输出结果为:max=30
看似和例9-4得到了一样的结果,但仔细分析其替换及计算过程,二者有很大差别。注意参数的替换过程就是用实参简单替换形参而不会对实参做任何处理,而这里的实参分别是10+10和10+20,因此z=MAX(10+10,10+20)语句替换后的结果为:
z=10+10>10+20?10+10:10+20;
此语句等价于:z=10+(10>10)+(20?20:30);
所以最后结果为:z=30。(www.xing528.com)
虽然结果相同,但宏替换后的运算过程显然是不一样的,这在程序中会带来大问题。
【例9-5】利用带参数宏计算两个整数的乘积。
结果不是想象中的15,请大家结合带参数宏的替换过程自行分析。
由于实参可以是常量、变量及表达式,而带参数宏的展开过程仅仅是做一个简单的参数替换(这不同于函数),因此在定义带参数宏时对宏定义字符串中的参数都要用括号括起来(整个字符串部分最好也用括号括起来),这样才能保证在任何情况下把定义作为一个整体来看待。
如将例9-5中的宏定义修改为:
#define M(a,b)((a)*(b))
则M(3+2,2+1)会被替换成((3+2)*(2+1)),其结果为15。
在编写程序时,也可以将程序中经常用到的语句序列定义为带参数的宏,在发生宏调用时,直接把这些代码替换到源程序内。
【例9-6】输入两个整数并按照从小到大的顺序输出。
结合前边例子,将带参数的宏使用时要注意的事项总结如下。
(1)在定义带参数的宏时,宏名和形参之间不能加空格,否则C编译系统会将空格以后的所有字符均作为“宏值”而将该宏视为无参数宏定义。
如定义:#define MAX(a,b)(a)>(b)?(a):(b)
则在宏替换时会把MAX作为宏名而替换为(a,b)(a)>(b)?(a):(b),这在编译时会引发问题。
(2)带参数的宏定义中,形式参数仅做替换使用,不会被分配内存单元,因此不用定义其数据类型。
(3)宏定义中的形参是一个标识符,宏调用时的实参可以是常量、变量、表达式,但在参数替换(不是参数传递)过程中不求解实参的值而只是用实参简单替换形参,因此一般要求在定义带参数宏时要将字符串中的形参用括号括起来。
最后简单总结一下带参数宏和有参数的函数的区别,二者虽然在调用形式上看起来很类似,但在运行机制上有本质区别,主要表现在:
(1)函数调用时要先求出实参的值然后将值传递给形参,而展开有参宏时只是进行简单的字符串的替换同时用实参置换形参,不存在参数的传递问题,更不会计算实参的值。
(2)函数调用是在程序执行过程中被执行,而宏展开是在编译前处理完毕,属于预处理。
(3)函数中要求实参和形参都要定义类型且相对应的参数类型应该一致,函数运行过程中实参和形参都会被分配内存。而在有参数宏中,实参和形参不分配内存单元,因此不必作类型定义,形参仅仅是一个符号,展开时用定义的字符串替换即可。
(4)发生函数调用时系统只是将流程转向该函数并执行一遍其代码,因此无论函数被调用多少次都不会使目标程序变长。使用有参数宏时要执行字符串的替换,每替换一次就会使目标程序增大一次,因此一般可用宏来表示一些简单的表达式。
(5)一个函数只能有一个返回值,但是通过一次宏调用可能能够得到多个值。
总之,对于同样的功能需求,编程时能够用函数来完成,也可能能够使用带参数的宏来完成,明白二者之间的区别后,请大家结合二者特点慎重考虑和选择。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。