![b417dd30866c4b671c790ec1436489bd.png](https://i-blog.csdnimg.cn/blog_migrate/7f02d654c710aea7e3bf133ddf073f89.jpeg)
宏定义是用一个标识符来表示一个字符串,在宏调用中将用该字符串代替宏名。给程序员提供了便利,使程序更加清晰,便于阅读和理解,进一步提高了程序的运行效率,对于嵌入式系统而言,为了能达到性能要求,宏是一种很好的代替函数的方法,但是使用不慎会得到意外的结果。
宏是C语言中常用的编译预处理功能之一。在编程时,可以使用宏来代替一些常量或表达式,给程序员提供了便利,使程序更加清晰,便于阅读和理解,进一步提高了程序的运行效率。另外,在C语言中,宏是产生内嵌代码的唯一方法,并且可以定义带参数的宏,对于嵌入式系统而言,为了能达到性能要求,宏是一种很好的代替函数的方法。但是如果对宏命令本质理解不透彻,在使用时可能运行的结果与预期的结果就会大相径庭。下面具体介绍C语言中宏的使用(即宏定义、宏调用、宏展开)。
一、宏定义
在C语言源程序中,允许用一个标识符表示一个字符串,称为“宏”;被定义为宏的标识符称为“宏名”。
宏定义是由源程序中的宏定义命令完成的。该命令有两种形式:一种是无参数的宏定义;另外一种是带参数的宏定义。
(一)无参数的宏定义
无参数宏的宏名后不带参数。其定义的一般形式为:
#define标识符字符串
#是预处理命令的标志,define是宏定义命令的标志。标识符为宏名,字符串可以是常量、表达式、格式串等。
例如:#definePI3.1415926
#defineSUM (1+2)
(二)带参数的宏定义
带参数的宏定义,也称为宏函数,在宏定义中的参数称为形式参数,形式参数不分配内存单元,所以不必作类型定义。带参数的宏定义的一般形式如下:
#define宏名(参数表)宏体
例如:#defineAREAR(R)3.14*R*R
#define SUM(X,Y) X+Y
两种格式的宏定义必须写在函数的外边,其作用域为宏定义命令起到源程序结束,若要终止其作用域可以用#undef命令加宏名,宏定义也允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。
![f9fb7a3745cf8aa6bd6fb2777a503e7f.png](https://i-blog.csdnimg.cn/blog_migrate/ea15a899d9ab5ae1e7228ec3b27e4898.jpeg)
二、宏展开及宏调用
(一)宏展开及调用的概念
编译预处理时程序中出现的所有宏名都要有宏定义中的字符串来代换,称为宏展开。嵌套的宏定义,展开时要层层展开。程序中字符串内的字符跟宏名相同时作为一般字符处理,不用宏展开。对于带参数的宏的使用称为宏调用,一般形式为:宏名(实参表);在调用中,不仅要宏展开,而且要用实参去代换形参。
C语言源程序的处理过程,经历了编辑、预处理、编译、连接几个过程。其中预处理产生编译的输入,源文件中若有宏命令,则预处理器将其展开成相应的宏定义,这个阶段的工作只是纯粹的替换展开,没进行任何的计算。同时现有的编译系统没有提供独立的预处理系统进行预处理,所以我们看不到预处理后的代码,在编译时可能会出现错误,但是系统检测不出来。所以在宏展开时我们一定要谨慎。
(二)宏展开及调用的应用实例
1.无参宏的定义实例
例题1:求圆的周长及面积。
源程序:
#definePI3.14159
main()
{float r,c,s;
scanf(“%f”,&r);
c=2*PI*r;
s=PI*r*r;
printf(“周长为:%f,面积为:%f”,c,s);}
本题中的PI为宏名,在程序中出现宏名的地方直接用字符串3.14159代替即可得宏展开后的程序,并能计算出结果。
例题2:源程序:
#defineN2+1
main()
{int a;
a=N*N;
Printf( % d,a); }
本题中宏名N代表的是表达式2+1,使用时容易产生的错误是先把N计算出来2+1=3,然后再带入求a的值,a=3*3,结果为9。但实际上宏展开只是简单的代替,不进行计算,本题应该这样计算a=2+1*2+1,得到结果为5。
2.带参宏的定义实例
例题3:源程序:
#define f(x)(x*x)
main()
{inti1, i2;
i1=f(8)/f(4);
i2=f(4+4)/f(2+2);
Printf(%d,%d,i1,i2);}
本题的输出结果是4,3。i1,i2看上去参数一样为什么结果不同呢,实际上二者的调用、展开形式不同,其中i1=(8*8)/(4*4)=64/16=4,i2=(4+4*4+4)/(2+2*2+2)=24/8=3;和例题2一样宏展开只是简单的代替,不先进行计算。
3.宏使用中问题的解决方法。若想正确的使用好宏,应当做到如下两方面:一方面在读别人的程序时,先将程序中对宏的使用全部替换成它所代表的字符串,不要添加任何其他符号,完全展开后再进行计算。另一方面在自己编程时,简单的无参宏定义中当字符串中不只一个字符时,加上括号表现出优先级,带参的宏定义则要给宏体中的每一个参数加上括号,并在整个宏体上再加上一个括号。
看到这里有人不禁会想,宏使用时那么麻烦、谨慎,我们为什么还要使用它,把它摈弃掉算了。但是宏定义的使用有很多的好处。
三、宏定义的优点
(一)简化程序提高可读性
使用宏可以将程序中的常量用有意义的符号代替,当常量比较长时, 以用较短的有意义的标识符来写程序, 这样更方便一些。当常量值需要改变时,不必在整个程序中查找常量,只需改变宏定义就可以方便地修改常量值,因此程序修改方便;使程序更加清晰,便于阅读和理解; 所说的常量改变不是在程序运行期间改变而是在编程期间的修改。
(二)提高程序的运行效率
更为有效的是C语言允许带参数宏定义,C语言带参数宏定义可以代替简单的函数,完成类似函数调用的功能。虽然函数的使用可以使程序更加模块化,便于组织,而且可重复利用。但在函数调用时,需要保留调用函数的现场,以便子函数执行结束后能返回继续执行,同样在子函数执行完后要恢复调用函数的现场,这都需要一定的时间。如果子函数执行的操作比较多,这种转换时间就会更大,而参数宏定义在宏调用展开后相当于直接嵌入代码 ,比函数调用具有更高的时间效率。使用带参数的宏,不占有内存,即能减少系统内存的使用,提高运行效率。
![f88a6876d746262c693da5954ea9ccb2.png](https://i-blog.csdnimg.cn/blog_migrate/e762c13c25cf8426926844ee27141edc.jpeg)
四、结束语
本文对C语言中宏定义的使用进行了分析,并从C语言源程序处理过程的角度对宏的处理进行了分析,提出了避免错误的方法,也对它的优点进行了阐述。只要能够理解宏展开的规则,就能够体会宏使用带来的方便和效率了。
学习C/C++编程知识,想要成为一个更加优秀的程序员,或者你学习C/C++的时候有难度,可以来C++编程学习圈,和别人一起交流成长会比自己琢磨更快哦!
学习C/C++编程知识,欢迎关注笔者的学习专栏:
C/C++学习笔记zhuanlan.zhihu.com![4dc5e1b5d3d66f2bbdba7b59d0c45dd9.png](https://i-blog.csdnimg.cn/blog_migrate/19006cbc03cc834125892437a6db33fb.jpeg)