第9章编译预处理_第1页
第9章编译预处理_第2页
第9章编译预处理_第3页
第9章编译预处理_第4页
第9章编译预处理_第5页
已阅读5页,还剩53页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

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

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

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

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

5、符号常量的可读性强。例如:#define LENGTH 20#define WIDTH 40#define HEIGTH 60在程序中用LENGTH、WIDTH、HEIGTH时,一看就知道它们分别代表长、宽、高,而如果直接用20、40、60,则很难猜出它们是长、宽、高。第第9章章 编译预处理编译预处理2) 增强程序的可维护性 如果一个常量在程序中多次被引用,则可把它定义为符号常量。这样,在以后需改动该常量时,只需改动它的宏定义语句即可,而不必对每一个引用它的地方进行修改。这不但可以减少修改的工作量,而且可以避免漏改。3. 无参数宏的注意事项 使用无参数宏定义符号常量时,一般应注意以下几点。 (

6、1) 宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查。如有错误,则只能在编译已被宏展开后的源程序时发现。第第9章章 编译预处理编译预处理(2) 符号常量名一般用大写字母(也可以用小写字母)表示,以便与其他标识符相区别。符号常量名的命名规则与一般标识符相同。另外,应考虑在字符串中根据需要加上括号。 (3) 宏定义不是说明或语句,因此,不能用分号结尾。如果加上分号,则分号被作为字符串的一部分,连分号也一起置换。例如:#define A 60 ; 上面的格式表示A被定义为“60 ;”

7、,而不是“60”。于是,在预编译时,程序中凡是出现A的地方,都用“60 ;”替换。这就不符合原意了。 (4) 替换字符串可以为空。第第9章章 编译预处理编译预处理(5) 宏定义语句应放在函数定义之外,符号常量的有效范围是从定义它的宏定义语句开始至所在源文件的结尾。一般宏定义语句都放在源文件的开头,以便使它对整个源文件都有效。(6) 为了灵活控制宏定义的作用范围,可用“undef”命令终止宏定义的作用域。第第9章章 编译预处理编译预处理例如:#define PI 3.14159main( ) # undef PI /* PI的宏定义结束 */f1( ) 表示PI只在main函数中有效,在f1中无

8、效。 第第9章章 编译预处理编译预处理 (7) 宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时由预处理程序层层代换。例如:#define PI 3.14 #define R 10 #define S PI*R*Rmain( ) printf(S=%f , S);预编译后,该程序变为main( ) printf(S=%f , 3.14*10*10);第第9章章 编译预处理编译预处理(8) 宏名在源程序中若用引号括起来,则预处理程序不对其作宏代换。例如:#define NO 220main( ) printf(NO); printf(n);上例中定义宏名NO表示220,但在p

9、rintf语句中NO被引号括起来,因此不作宏代换。程序的运行结果为NO表示把“NO”当字符串处理。第第9章章 编译预处理编译预处理 【例9-1】假设血压正常值低压为70,高压为120。如果低压高于70,并且高压低于120,则为正常。从键盘输入血压值,判断该血压值是否正常。#include main( )#define LOW 70/* 定义宏 */#define HIGH 120/* 定义宏 */第第9章章 编译预处理编译预处理 int bloodplow , bloodphigh ; do scanf(%d%d , &bloodphigh , &bloodplow) ; wh

10、ile(bloodplow=bloodphigh) ; if(bloodplowLOW & bloodphighHIGH) printf(you are right!) ; else printf(you may have something wrong!) ; 输入:130 80 输出:you may have something wrong!第第9章章 编译预处理编译预处理 【例9-2】已知一梯形的上下两边的长分别为a、b,输入高h,求其面积。#include main( )#define a 5/* 定义宏 */#define b 15/* 定义宏 */#define L (a+

11、b)/* 嵌套定义宏 */第第9章章 编译预处理编译预处理 float h , s ; scanf(%f , &h) ; s=h*L/2 ; printf(s=%f n , s) ; 输入:3 输出:s=30.000000第第9章章 编译预处理编译预处理【例9-3】利用迭代法求方程的根。其迭代公式为yi+1=(yi+x/yi)/2。#include stdio.hmain( )#define ABS 1e-4 float y1 , y , x ; printf(x=) ; scanf(%f ,& x) ; printf(y=) ; scanf(%f , &y) ; do

12、第第9章章 编译预处理编译预处理 y1=y ; y=(y1+x/y1)/2 ; while(y1-y)ABS) ; printf(%fn , y) ; 运行结果:x=2y=31.414214第第9章章 编译预处理编译预处理9.1.2 带参数的宏定义1. 带参数的宏的定义 利用#define语句不仅可以定义符号常量,也可以定义带参数的宏。带参数的宏的一般定义格式为#define 宏名(参数表) 字符串字符串中包含参数表中的参数。 调用带参数宏的一般格式为宏名(实参表); 例如:#define MIN(a,b) (a)(b)? (a) : (b)第第9章章 编译预处理编译预处理其中,MIN(a,b

13、)是带参数的宏,a和b是形式参数。该定义把MIN(a,b)定义为“(a)(b)? (a):(b)”。在定义了该宏后,就可在程序中用MIN(a,b)替代定义它的运算表达式“(a)(b)? (a):(b)”。宏的使用方法类似函数。例如,在需要求两个数的最小值时,就可以使用已定义的宏。c= MIN(10,20) ; 在进行编译时,预编译程序根据宏定义式来替换程序中出现的带参数的宏,其中定义式中的形式参数用相应的实际参数替换。于是,上面的赋值语句变为c= MIN(10=0)? (x): -(x)/* 求x的绝对值 */#define MAX(a,b) (a)b)?a:b改写为 #define MAX(

14、a,b) (ab)?a:b将被认为是无参宏定义,宏名MAX代表字符串“(a,b) (ab)?a:b”。宏展开时,宏调用语句:max=MAX(x,y);将变为 max=(a,b)(ab)?a:b(x,y);这显然是错误的。第第9章章 编译预处理编译预处理【例9-4】键盘输入立方体的边长a,求其表面积s及体积v。#include main( )#define L(a,s,v) s=6*a*a ; v=a*a*a int a1,s1,v1; a1=0 ; scanf(%d,&a1) ; L(a1,s1,v1) ; printf(%d , %d , %dn , a1 , s1 , v1) ;第

15、第9章章 编译预处理编译预处理【例9-5】求1100所有奇数的和。#include main( )#define ISODD(x) (x)%2=1)? 1:0) int sum , i ; sum=1 ; for(i=3 ; i=100 ; i+) if ISODD(i) sum+=i ; printf(%dn , sum) ;第第9章章 编译预处理编译预处理【例9-6】已知某单位上缴个人所得税的算法如下:输入工资,求其应上缴的税款。#include main( )#define TAX1(a) (a=800) ? 0 : (a1300)? 0.05 : (a=2800) ? 0.1 : (a

16、=5800) ? 0.15 : 0.2)#define TAX2(b) (b=1300) ? 0 : (b=2800)? 25 : (b=5800)? 125 : 375)#define TAX3(c) c*TAX1(c)-TAX2(c)第第9章章 编译预处理编译预处理 float tax , wage ; scanf(%f , &wage) ; tax=TAX3(wage) ; printf(%f , %fn , wage , tax) ;第第9章章 编译预处理编译预处理【例9-7】输入4个整数,按由小到大的顺序输出。#include main( )#define CHANGE(a,

17、b) int t ; t=a; a=b; b=t ; int i , j , k , l ; printf(please input four number:) ; scanf(%d , %d , %d , %d , &i , &j , &k , &l) ;第第9章章 编译预处理编译预处理if(ij) CHANGE(i , j) ; if(ik) CHANGE (i , k) ; if(il) CHANGE (i , l) ; if(jk) CHANGE (j , k) ; if(jl) CHANGE (j , l) ; if(kl) CHANGE (k , l

18、) ; printf(the proper order is:) ; printf(%d %d %d %dn , i , j , k , l) ;第第9章章 编译预处理编译预处理9.2 文件包含文件包含在前面章节中,我们经常在编写程序中,会写上下面的语句:#include 其含义是在编译时,用stdio.h头文件的内容替换该语句。 文件包含语句的一般格式为#include 文件名或 #include 第第9章章 编译预处理编译预处理其中,是被包含文件的文件名,它是一个磁盘文件。该预编译语句的功能是要将所指文件的全部内容包含在该#include语句所在的源文件中。也就是说,在预编译时,用所指文件

19、的全部内容替换该#include语句行,使该文件成为这个源文件的一部分。 在#include语句的书写格式中,被包含文件的文件名可用尖括号()括住,也可以用双引号( )括住。 当用尖括号括住时,表示编译系统按系统设定的标准目录搜索文件;当用双引号括住时,表示按指定的路径搜索。若未指定路径名时,则在当前目录中搜索。第第9章章 编译预处理编译预处理文件包含语句是很有用的语句,特别是对包括多个源文件的大程序来说,可以把各个源文件中共同使用的函数说明、符号常量定义、外部量说明、宏定义和结构类型定义等写成一个独立的包含文件,在需要这些说明的源文件中,只需在源文件的开头用一个#include语句把该文件包

20、括进来,这样就可以避免重复工作。例如:/* file1.c */#include file2.hmain( ) /*file2.h*/#define PI 3.14159第第9章章 编译预处理编译预处理 做成包含文件的另一个好处是,当这些常量、宏定义等需要修改时,只需修改这个被包含的文件即可,而不必修改各源文件。 使用#include语句时,应注意以下两点: (1) 一个include命令只能指定一个被包含文件,若有多个文件要包含,则需用多个include命令。 (2) 文件包含允许嵌套,即在一个被包含的文件中又可以包含另一个文件。第第9章章 编译预处理编译预处理9.3 条件编译条件编译一般情

21、况下,源程序中所有的行都参加编译,但有时在写程序时要求根据具体情况编译不同的程序代码,C语言中提供了条件编译,可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。这对于程序的移植和调试是很有用的。条件编译有3种形式,下面分别介绍。第第9章章 编译预处理编译预处理1. #ifdef #else #endif语句 用# ifdef # else # endif语句进行条件编译的指令格式为#ifdef 标识符 程序段1#else 程序段2#endif其作用是:如果标识符已被定义(用#define定义),则对程序段1进行编译,而程序段2被删除;否则,程序段1被删除,编译程序段2。其中,#

22、else部分是可以缺省的,即第第9章章 编译预处理编译预处理#ifdef 标识符 程序段1#endif 条件编译语句中的#ifdef和#endif决定了编译范围,在此范围外的源程序不存在条件编译问题。条件编译对于提高程序的移植性很有帮助。第第9章章 编译预处理编译预处理【例9-8】条件编译#ifdef的使用。#include stdio.h#define TED 10main ( ) #ifdef TED printf(Hi Tedn); /* 如果定义了TED,则编译此行代码 */ #else printf(Hi anyonen); /* 如果没用定义TED,则编译此行代码 */第第9章章

23、编译预处理编译预处理printf(Hi anyonen); /* 如果没用定义TED,则编译此行代码 */ #endif #ifndef RALPH printf (RALPH not definedn); /* 如果定义了RALPH,则编译此行代码 */ #endif 上述代码打印“Hi Ted”及“RALPH not defined”。如果TED没有定义,则显示“Hi anyone”,后面是“RALPH not defined”。可以像嵌套#if 那样将#ifdef 与# ifndef 嵌套至任意深度。第第9章章 编译预处理编译预处理2. #ifndef #else #endif语句 由#

24、ifndef #else #endif语句进行条件编译的指令格式为#ifndef 标识符 程序段1#else 程序段2#endif与第一种形式的区别是将“ifdef”改为“ifndef”。它的功能是:如果标识符未被#define命令定义,则对程序段1进行编译,否则对程序段2进行编译。这与第一种形式的功能正相反。例如:第第9章章 编译预处理编译预处理#ifndef UNPRN printf(Name= %s sa= %f, name , s) ; #else printf(%s %f , name , s) ; #endif当UNPRN在程序段之前未定义时,则只编译“printf(Name= %

25、s sa= %f, name , s) ;”。如果在该程序段之前加一行:#define UNPRN 1则只编译“printf(%s %f , name , s) ;”。其中,UNPRN可定义为任何字符串。第第9章章 编译预处理编译预处理3. #if #else #endif语句 由#if # else # endif语句进行条件编译的指令格式为#if 表达式 程序段1#else 程序段2#endif其作用是:当表达式的值为非0时,编译程序段1,不编译程序段2;否则编译程序段2(其中#else部分是可以缺省的)。例如,在程序设计的测试阶段,经常要显示一些变量的信息,以检查是否正确,而在正式执行时

26、,却不需要显示这些信息。这时,就可以用下面形式的条件编译。 第第9章章 编译预处理编译预处理#define DEBUG 1#if DEBUG printf(a=%d b=%f c=%s , a , b , c) ; #endif以上形式的条件编译适用测试阶段,如果程序测试完成,在编译正式的执行代码时,只需把DEBUG定义为0即可。在进行条件编译时,可根据情况选择条件编译语句。第第9章章 编译预处理编译预处理【例9-9】条件编译#if的使用。#include stdio.h#define R 1main( ) float c,r,s; printf (input a number: ); sca

27、nf(%f,&c); #if R第第9章章 编译预处理编译预处理r=3.14159*c*c; /* 如果R非0,则编译此行和下一行代码 */ printf(area of round is: %fn,r); #else s=c*c; /* 如果R为0,则编译此行和下一行代码 */ printf(area of square is: %fn,s); #endif第第9章章 编译预处理编译预处理 本例中采用了第3种形式的条件编译。在程序第2行宏定义中,定义R为1,因此在条件编译时,常量表达式的值为真,故计算并输出圆面积。 上面介绍的条件编译当然也可以用条件语句来实现。但是用条件语句将会对整个源程序进行编译,生成的目标代码程序很长,而采用条件编译,则根据条件只编译其中的程序段1或程序段2,生成的目标程序较短。如果条件选择的程序段很长,采用条件编译的方

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论