2022年编译预处理[归 .pdf
《2022年编译预处理[归 .pdf》由会员分享,可在线阅读,更多相关《2022年编译预处理[归 .pdf(14页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、8 编译预处理 本章重点: 宏定义命令 条件编译命令 文件包含命令 本章难点: 带参宏定义 条件编译 在 C语言源程序中,除了为实现程序功能而使用的声明语句和执行语句之外,还可以使用编译预处理命令。所谓编译预处理是指,在对源程序进行编译之前,先对源程序中的编译预处理命令进行处理;然后再将处理的结果和源程序一起进行编译,得到目标代码。 C 语言提供的编译预处理命令主要有宏定义、条件编译和文件包含等三种。为了能够和一般 C语句区别开来,编译预处理命令以“#”号开头。它占用一个单独的书写行,命令行末尾没有分号。编译预处理是语言的一个重要功能,合理地使用编译预处理功能编写的程序便于阅读、修改、调试和移
2、植,也有利于模块化程序设计。 8.1 宏定义 宏定义是指将一个标识符( 又称宏名 ) 定义为一个字符串 ( 或称替换文本 ) 。在编译预处理时,对程序中出现的所有宏名都用相应的替换文本去替换,这称为“宏替换”或“宏展开” 。 在语言中,“宏定义”可分为无参宏定义和带参宏定义两种。8.1.1 无参宏定义 无参宏定义即定义没有参数的“宏”,其一般形式为: #define 标识符 替换文本 其中#define表示该语句行是宏定义命令,“标识符”为所定义的宏名,习惯上宏名用大写字母表示;“替换文本” 可以是常量、 关键字、 表达式、 语句等任意字符串。在 define 、宏名和替换文本之间分别用空格隔
3、开。 #define命令可以不包含“替换文本”,此时仅说明宏名已被定义,以后可以使用。 第 2 章介绍的符号常量的定义就是一种无参宏定义。 例 8-1 用无参宏定义计算s=3*(y*y+3*y)+4*(y*y+3*y)+5*(y*y+3*y)。 算法分析:在计算式子中出现了三个(y*y+3*y) ,为减少书写量,可使用宏定义。程序如下:名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 1 页,共 14 页 - - - - - - - - - #define M (y*y+3*y) ma
4、in() int s,y; printf(Please input a number: ); scanf(%d ,&y); s=3*M+4*M+5*M; printf(s=%dn,s); 运行情况如下: Please input a number: 4 s=336 在上面程序的语句s=3*M+4*M+5*M; 中引用了3 次宏 M ,经“宏展开”后该语句变为: s=3*(y*y+3*y)+4*(y*y+3*y)+5*(y*y+3*y); 符合题目要求。注意在宏定义中替换文本(y*y+3*y) 两边的括号不能少,否则会产生错误。如改为以下定义: #difine M y*y+3*y 则在宏展开时将
5、得到下述语句: s=3*y*y+3*y+4*y*y+3*y+5*y*y+3*y; 显然与原题意要求不符,计算结果当然是错误的。因此在进行宏定义时必须注意,应保证在宏代换之后不发生错误。 对于无参宏定义还要说明以下几点: (1) 习惯上宏名用大写字母表示,以便与变量名区别开。但这并非规定,也允许使用小写字母。 (2) 用替换文本替换宏名只是一种简单的直接替换,替换文本中可以包含任意字符,系统在进行编译预处理时对它不作任何检查。例如: #define PI 3.l415926 即不小心将替换文本中的第一个数字“1”错写成了小写字母“ l ”,不再表示圆周率的近似值,预处理时仍然把PI 替换成 3.
6、l415926 ,而不管含义如何。在编译时才发现错误并报错。 (3) 宏定义不是声明或执行语句,在行末不要加分号,如果加上分号则连分号也一起替换。 (4) 一个#define只能定义个宏,且一行只能定义一个宏。若需要定义多个宏就要使用多个 #define ,并写在多行上。 (5) 宏定义时如果一行写不下,可用“ ”续行。 例如: #define PI 3.1415926 /*正确*/ #define PI 3.1415 926 /*正确*/ (6) 宏定义原则上可以出现在源程序的任何地方,但通常写在函数之外,其作用域为名师资料总结 - - -精品资料欢迎下载 - - - - - - - - -
7、 - - - - - - - - - 名师精心整理 - - - - - - - 第 2 页,共 14 页 - - - - - - - - - 从宏定义命令起到源程序文件结束。如要终止其作用域可使用#undef命令,其用法为: #undef 标识符如:#undef PI (7) 宏名在源程序中若用双撇号括起来,则在编译预处理时不对其作宏替换。也就是说,宏名被双撇号括起来时,仅作为一般字符串使用。 例 8-2 宏替换的选择性。 #define PI 3.1415926 main() printf(PI is %9.7f.n,PI); 程序运行结果为: PI is 3.1415926. 例 8-2
8、中定义宏名PI 表示 3.1415926,但在 printf函数中第一个PI 被双撇号括起来,因此不进行宏替换, 只把该“ PI”当普通的字符串处理,而第二个 PI 没有被双撇号括起来,因此被替换成了3.1415926。 (8) 宏定义允许嵌套,在宏定义的替换文本中可以使用已经定义过的宏名。在宏展开时层层替换。 例如: #define PI 3.1415926 #define S PI*r*r /* PI是已定义的宏名 */ 对语句: printf(%f,S); 在宏替换后变为: printf(%f,3.1415926*r*r); 使用无参宏定义还可以实现程序的个性化( 如用自己所习惯的符号表
9、示数据类型或输出格式等 ) ,使程序的书写、阅读更加方便。 例 8-3 用无参宏定义表示常用的数据类型和输出格式。#define INTEGER int #define REAL float #define P printf #define D %dn#define F %fnmain() INTEGER a=5, c=8, e=11; REAL b=3.8, d=9.7, f=21.08; P(D F,a,b); P(D F,c,d); P(D F,e,f); 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 -
10、- - - - - - 第 3 页,共 14 页 - - - - - - - - - 程序运行结果为: 5 3.800000 8 9.700000 11 21.080000 8.1.2 带参宏定义 语言允许“宏”带有参数。在宏定义中的参数称为形式参数,在引用带参宏时给出的参数称为实际参数。 带参宏定义的一般形式为: #define 宏名( 形参表 ) 替换文本其中,形参表由一个或多个形参组成,各形参之间用逗号隔开,替换文本中通常应包括有形参。 引用带参宏的一般形式为: 宏名( 实参表)带参宏定义展开时先把宏引用替换为替换文本,再将替换文本中出现的形参用实参代替。例如下面的宏定义和引用: #de
11、fine M(y) y*y+3*y /*宏定义 */ k=M(5); /*宏引用 */ 宏展开时,先用y*y+3*y 替换 M(5),再将替换文本中的形参y 用实参 5 代替,最终得到: k=5*5+3*5; 例 8-4 用带参宏定义求两数中的大者。#define MAX(a,b) (ab)?a:b main() int x,y,max; printf(input two numbers(x,y): ); scanf(%d,%d ,&x,&y); max=MAX(x,y); printf(max=%dn ,max); 程序运行情况如下: input two numbers(x,y): 5,6
12、max=6 这里的宏MAX(a,b)既可以比较int型数据,也可以比较float型、char 型等各种类型数据。若要比较float型数据,只需将程序第3 行改为: 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 4 页,共 14 页 - - - - - - - - - float x,y,max; 并在输入输出格式控制处将“%d ”改为“ %f”即可,宏定义无需改动。 如果用函数实现上述功能,则需要写相应的两个函数才可以。 对于带参宏定义,除了需要遵守一些与无参宏定义一样的规则,如一
13、个#define命令只能定义一个带参宏,通常在函数外定义,允许嵌套、续行、使用#undef命令终止宏定义等,另外还应注意以下几点: (1) 在带参宏定义中,宏名与其后的左括弧“( ”之间不得有空格,否则将变为无参宏定义。 例如把: #define MAX(a,b) (ab)?a:b 写为: #define MAX (a,b) (ab)?a:b 将被认为是无参宏定义,宏名是MAX ,替换文本为 (a,b) (ab)?a:b。对下面的语句:max=MAX(x,y); 进行宏替换后,将变为: max=(a,b) (ab)?a:b(x,y); 这显然是错误的。 (2) 在带参宏定义中,替换文本中的形参
14、通常要用括号括起来以避免出错。 例 8-5 分别引用以下宏定义,求3*F(3+2) 的值。 (A) #define F(x) x*x+x (B) #define F(x) (x)*(x)+(x) (C) #define F(x) (x*x+x) (D) #define F(x) (x)*(x)+(x) 解:表达式 3*F(3+2) 在分别引用以上4 个宏定义后,其值为: (A) 22 。因为宏定义只作为一种简单的字符替换,所以在引用(A) 中的宏定义后,表达式 3*F(3+2) 被替换为3*3+2*3+2+3+2。 (B) 80 。表达式3*F(3+2) 被替换为: 3*(3+2)*(3+2)
15、+(3+2)。 (C) 48 。表达式3*F(3+2) 被替换为: 3*(3+2*3+2+3+2) 。 (D) 90 。表达式3*F(3+2) 被替换为: 3*(3+2)*(3+2)+(3+2)。 由此可见, 使用带参数的宏定义,替换文本中的括号位置不同,可以得出不同的结果。使用时一定要仔细考虑。 (3) 宏定义也可用来定义多个语句,在宏替换时,把这些语句都替换到源程序中。 例 8-6 一个宏定义代表多个语句。#define SSSV(s1,s2,s3,v) s1=l*w; s2=l*h; s3=w*h; v=w*l*h; main() int l=3,w=4,h=5,sa,sb,sc,vv;
16、 SSSV(sa,sb,sc,vv); printf(sa=%dnsb=%dnsc=%dnvv=%dn,sa,sb,sc,vv); 程序运行结果为: 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 5 页,共 14 页 - - - - - - - - - sa=12 sb=15 sc=20 vv=60 程序第一行为宏定义,用宏名 SSSV 表示 4 个赋值语句, 4个形参分别为4个赋值符左边的变量。在宏替换时,把4 个语句展开并用实参代替形参,得到计算结果。 应该注意的是,带参宏定义
17、和函数有一定的相似之处。如表示形式都是由一个名字加上参数表组成,都要求实参与形参的数目相同等。因此,很多读者容易将它们混淆。下面将带参宏定义与函数的主要区别列出,以帮助读者更快地掌握带参宏定义。 带参宏定义与函数的主要区别如下: (1) 定义方式不同。带参宏使用预处理命令#define定义;而函数使用函数定义。 (2) 参数性质不同。带参宏的参数表中的参数不必说明其类型,也不分配存储空间;而函数参数表中的参数需说明其类型并为其分配存储空间。 (3) 实现方式不同。宏展开是在编译时由预处理程序完成的,不占用运行时间;而函数调用是在程序运行时进行,需占用一定的运行时间。 (4) 参数传递不同。若实
18、参为表达式,引用带参宏时只进行简单的字符替换,不计算实参表达式的值;而函数调用时,则先计算表达式的值,然后代入形参。 (5) 返回值不同。带参宏定义无返回值;而函数有返回值。 例 8-7 带参宏定义的实参是表达式的情况。#define SQ(y) (y)*(y) main() int a,sq; printf(input a number: ); scanf(%d ,&a); sq=SQ(a+1); printf(sq=%dn,sq); 程序运行情况如下: input a number: 3 sq=16 程序中定义了带参宏SQ(y),在引用时实参为表达式a+1。在宏展开时,先用 (y)*(y)
19、 替换 SQ(a+1),再用实参表达式a+1 替换形参y,最后得到如下语句: sq=(a+1)*(a+1); 这与函数的调用是不同的,函数调用时要先把实参表达式的值求出来,再赋予形参。而宏引用时对实参表达式不作计算,直接照原样替换。可简单总结为“完全展开,直接代替” 。 例 8-8 函数与带参宏定义的进一步比较。#define SQ_MACRO(y) (y)*(y) main() 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 6 页,共 14 页 - - - - - - - - -
20、 int i=1; printf(SQ_fun:n ); while(i=5) printf(%dn,SQ_fun(i+); i=1; printf(SQ_MACRO:n ); while(i=5) printf(%dn,SQ_MACRO(i+); SQ_fun(int y) return(y)*(y); 程序运行结果为: SQ_fun: 1 4 9 16 25 SQ_MACRO: 2 12 30 此题本意是用函数调用和宏引用来分别实现输出15 的平方值。 但程序中, 函数调用是把实参i 的值传给形参y 后自增 1,因而要循环5 次,输出 15的平方值。 而在宏引用时, SQ_m(i+)被替换
21、为 (i+)*(i+)。在第一次循环时,i等于 1,其计算过程为:表达式中前一个i 为 1,然后 i 自增 1 变为 2,此时表达式中第2 个 i 为 2,二者相乘的结果为2,然后 i 值再自增 1 为 3。在第二次循环时,i已为 3,因此表达式中前一个 i 为 3,后一个 i 为 4,乘积为 12,然后 i 再自增 1 变为 5。进入第三次循环,由于i 值已为 5,所以这将是最后一次循环。计算表达式的值为5*6 等于 30,并且 i 值变为 7,不再满足循环条件,跳出循环,最后只输出三个值,事与愿违。 从以上分析可以看出函数调用和宏引用二者虽然在形式上非常相似,但在本质上是完全不同的。 8.
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 2022年编译预处理归 2022 编译 预处理
限制150内