第9章编译预处理

上传人:今*** 文档编号:107486148 上传时间:2019-10-19 格式:PPT 页数:56 大小:440KB
返回 下载 相关 举报
第9章编译预处理_第1页
第1页 / 共56页
第9章编译预处理_第2页
第2页 / 共56页
第9章编译预处理_第3页
第3页 / 共56页
第9章编译预处理_第4页
第4页 / 共56页
第9章编译预处理_第5页
第5页 / 共56页
点击查看更多>>
资源描述

《第9章编译预处理》由会员分享,可在线阅读,更多相关《第9章编译预处理(56页珍藏版)》请在金锄头文库上搜索。

1、第9章 编译预处理,9.1 宏定义 9.2 文件包含 9.3 条件编译 习题9,9.1 宏定义,9.1.1 无参数的宏定义 无参数宏的宏名后不带参数。其定义的一般格式为 #define 宏名 字符串 其中的“#”表示这是一条预处理命令。凡是以“#”开头的均为预处理命令。“define”为宏定义命令。“宏名”为所定义的宏名。“字符串”可以是常数、表达式和格式串等。在前面介绍过的符号常量的定义就是一种无参宏定义。此外,常对程序中反复使用的表达式进行宏定义。,1. 无参数宏定义符号常量 符号常量用无参数的宏定义语句定义,把符号常量名定义为指定的字符串,将程序中出现宏名的地方均用该字符串来替换。在进行

2、编译预处理时,用该字符串替代程序中出现的符号常量名。例如: #define TRUE 1 #define FALSE 0 把TRUE定义为1,把FALSE定义为0。在符号常量定义之后,就可以用它来编码了。,例如: if(i=TRUE) printf(“you are right! n“) ; else if(i=FALSE) printf(“you are wrong! n“) ; 对于该程序段,在进行编译预处理时,就把程序中出现的TRUE和FALSE分别用1和0替代,于是就变为 if(i=1) printf(“you are right! n“) ; else if(i=0) printf(

3、“you are wrong! n“) ;,在符号常量定义语句中,字符串可以是一个数值型数据、表达式或字符串。例如: #define PI 3.1415926 #define S (PI*r*r) #define PRT printf #define A (20-(3*4) 如果字符串是一个运算表达式,一般应该用括号括住它,以便把它视为一个操作对象与其他操作数进行运算,否则,会由于操作优先级问题而发生错误。例如: text = A*8 ; 进行编译预处理后,该表达式变为 text = (20-(3*4)*8 ;,如果A定义为 #define A 20- (3*4) 则表达式text = A*8

4、经预编译后变为 text = 20- (3*4)*8 ; 这就不符合原意。因此,在宏定义语句中的字符串为一般表达式(而不是一个操作数)时,为了保证正确的运算次序,应该用括号括住它。因此在宏定义时必须十分注意,应保证在宏代换之后不发生错误。,2. 无参数宏的好处 在程序设计中,使用无参数的宏有下面两点好处: 1) 增强程序的可读性 以符号常量为例,由于符号常量含义明确,于是采用符号常量书写的程序要比不采用符号常量的可读性强。例如: #define LENGTH 20 #define WIDTH 40 #define HEIGTH 60 在程序中用LENGTH、WIDTH、HEIGTH时,一看就知

5、道它们分别代表长、宽、高,而如果直接用20、40、60,则很难猜出它们是长、宽、高。,2) 增强程序的可维护性 如果一个常量在程序中多次被引用,则可把它定义为符号常量。这样,在以后需改动该常量时,只需改动它的宏定义语句即可,而不必对每一个引用它的地方进行修改。这不但可以减少修改的工作量,而且可以避免漏改。 3. 无参数宏的注意事项 使用无参数宏定义符号常量时,一般应注意以下几点。 (1) 宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查。如有错误,则只能在编译已被宏展开后的源程序

6、时发现。,(2) 符号常量名一般用大写字母(也可以用小写字母)表示,以便与其他标识符相区别。符号常量名的命名规则与一般标识符相同。另外,应考虑在字符串中根据需要加上括号。 (3) 宏定义不是说明或语句,因此,不能用分号结尾。如果加上分号,则分号被作为字符串的一部分,连分号也一起置换。例如: #define A 60 ; 上面的格式表示A被定义为“60 ;”,而不是“60”。于是,在预编译时,程序中凡是出现A的地方,都用“60 ;”替换。这就不符合原意了。 (4) 替换字符串可以为空。,(5) 宏定义语句应放在函数定义之外,符号常量的有效范围是从定义它的宏定义语句开始至所在源文件的结尾。一般宏定

7、义语句都放在源文件的开头,以便使它对整个源文件都有效。 (6) 为了灵活控制宏定义的作用范围,可用“undef”命令终止宏定义的作用域。,例如: #define PI 3.14159 main( ) # undef PI /* PI的宏定义结束 */ f1( ) 表示PI只在main函数中有效,在f1中无效。,(7) 宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时由预处理程序层层代换。例如: #define PI 3.14 #define R 10 #define S PI*R*R main( ) printf(“S=%f “, S); 预编译后,该程序变为 main(

8、) printf(“S=%f “, 3.14*10*10); ,(8) 宏名在源程序中若用引号括起来,则预处理程序不对其作宏代换。例如: #define NO 220 main( ) printf(“NO“); printf(“n“); 上例中定义宏名NO表示220,但在printf语句中NO被引号括起来,因此不作宏代换。程序的运行结果为 NO 表示把“NO”当字符串处理。,【例9-1】假设血压正常值低压为70,高压为120。如果低压高于70,并且高压低于120,则为正常。从键盘输入血压值,判断该血压值是否正常。 #include main( ) #define LOW 70 /* 定义宏 *

9、/ #define HIGH 120 /* 定义宏 */, int bloodplow , bloodphigh ; do scanf(“%d%d“ , 输入:130 80 输出:you may have something wrong!,【例9-2】已知一梯形的上下两边的长分别为a、b,输入高h,求其面积。 #include main( ) #define a 5 /* 定义宏 */ #define b 15 /* 定义宏 */ #define L (a+b) /* 嵌套定义宏 */, float h , s ; scanf(“%f“ , 输入:3 输出:s=30.000000,【例9-3】

10、利用迭代法求方程的根。其迭代公式为yi+1=(yi+x/yi)/2。 #include “stdio.h“ main( ) #define ABS 1e-4 float y1 , y , x ; printf(“x=“) ; scanf(“%f“ , do, y1=y ; y=(y1+x/y1)/2 ; while(y1-y)ABS) ; printf(“%fn“ , y) ; 运行结果: x=2 y=3 1.414214,9.1.2 带参数的宏定义 1. 带参数的宏的定义 利用#define语句不仅可以定义符号常量,也可以定义带参数的宏。带参数的宏的一般定义格式为 #define 宏名(参数

11、表) 字符串 字符串中包含参数表中的参数。 调用带参数宏的一般格式为 宏名(实参表); 例如: #define MIN(a,b) (a)(b)? (a) : (b),其中,MIN(a,b)是带参数的宏,a和b是形式参数。该定义把MIN(a,b)定义为“(a)(b)? (a):(b)”。在定义了该宏后,就可在程序中用MIN(a,b)替代定义它的运算表达式“(a)(b)? (a):(b)”。宏的使用方法类似函数。例如,在需要求两个数的最小值时,就可以使用已定义的宏。 c= MIN(10,20) ; 在进行编译时,预编译程序根据宏定义式来替换程序中出现的带参数的宏,其中定义式中的形式参数用相应的实际

12、参数替换。于是,上面的赋值语句变为 c= MIN(1020) ? 10:20) ;,在程序设计中,经常要把反复使用的运算表达式定义为带参数的宏。例如: #define PER(a,b) (100.0*(a)/(b) /* 求a是b的百分之几 */ #define ABS(x) (x)=0)? (x): -(x) /* 求x的绝对值 */ #define MAX(a,b) (a)(b)? (a):(b) /* 求两个数中的较大数 */ #define ISO(x) (x)%2= =1)? 1:0) /* 判断是否为奇数 */,2. 带参数的宏的好处 使用带参数的宏有下面两点好处: (1) 使程序

13、更加简洁,减少不必要的重复书写。 (2) 增强程序的可读性,一般用一个含义明确的宏名代替一个较复杂的运算式,会使读者一目了然。 3. 带参数的宏与函数的区别 从上面的介绍中可以看出,带参数的宏和函数在使用形式和特性上都很相似。但是,二者又有本质区别,主要表现在以下几个方面。,(1) 函数调用时,要保留现场和返回点,而后把控制转移给被调用函数。当被调用函数执行结束后,又要恢复现场和把控制返回到调用函数。而对带参数宏的使用不存在控制的来回转移,它只是表达式的运算。 (2) 函数有一定的数据类型,且数据类型是不变的。而带参数的宏一般是一个运算表达式,它没有固定的数据类型,其数据类型就是表达式运算结果

14、的数据类型。同一个带参数的宏,随着使用实参类型的不同,其运算结果的类型也不同。 (3) 函数定义和调用中使用的形参和实参都受数据类型的限制,而带参数宏的形参和实参可以是任意的数据类型。,(4) 函数调用中存在参数的传递过程,而带参数宏的引用不存在参数传递过程。在函数中,形参和实参是两个不同的量,各有自己的作用域,调用时要把实参值赋予形参,进行“值传递”。而在带参宏中,只是符号代换,不存在值传递的问题。在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。例如: #define SQ(y) (y)*(y) main( ) int a,sq; printf(“input a number: “)

15、; scanf(“%d“, ,上例中第1行为宏定义,形参为y。程序第7行宏调用中实参为a+1,是一个表达式,在宏展开时,用a+1代换y,再用(y)*(y)代换SQ,得到如下语句: sq=(a+1)*(a+1) ; 这与函数的调用是不同的,函数调用时要把实参表达式的值求出来再赋予形参。而宏代换中对实参表达式不作计算直接地照原样代换。 (5) 使用函数可缩短程序占用的内存空间,但由于控制的来回转移,会使程序的执行效率降低。而带参数的宏则相反,多次使用宏会增加程序占用的存储空间,但其执行效率要比函数高。,除了使用运算表达式来定义带参数的宏外,还可使用函数来定义它,标准函数库中经常采用这种方式。例如:

16、 #define getchar( ) fgetc(stdin) #define putchar(ch) fputc(ch , stdout) (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; SSSV(sa,sb,sc,vv); printf(“sa=%dnsb=%dnsc=%dnvv=%dn“,sa,sb,sc,vv); ,程序第1行为宏定义,用宏名SSSV表示4个赋值语句,4个形参分别为4个赋值符左部的变量。在宏调用时,把4个语句展开并用实参代替形参。使计算结果送入实参之中。 4. 带参数的宏的注意事项 为了正确定义和使用带参数的宏,编程时应注意以下几点。 (1) 对宏定

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 高等教育 > 大学课件

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号