1、第 12 章 编 译 预 处 理 第第 12章章 编译预处理编译预处理 12.1 宏定义宏定义 12.2 文件包含文件包含12.3 条件编译条件编译 12.4 图图 形形 处处 理理第 12 章 编 译 预 处 理 12.1 宏定义宏定义 12.1.1 不带参数的宏定义不带参数的宏定义 定义形式:define 宏名 宏体 宏名是一自定义标识符,宏体是一字符串,在程序中可用宏名代表宏体。第 12 章 编 译 预 处 理 例如,用PI代表3.1415926,用PR代表printf,宏定义如下:(1)define PI 3.1415926 (2)definee PR printf 程序中凡是出现3.
2、1415926的地方,都可以以PI出现,凡是出现printf的地方,都可以以PR出现。第 12 章 编 译 预 处 理 例例 12-1 利用宏定义求圆的周长和面积。/*程序12-1,利用宏定义求圆的周长和面积 */define PI 3.1415926define R 1.0main()float l,s;l=2.0*PI*R;s=PI*R*R;PR(周长=%f,面积=%fn,l,s);第 12 章 编 译 预 处 理 上面程序在编译前将进行宏展开,宏展开以后变为:main()float l,s;l=2.0*3.1415926*1.0;s=3.1415926*1.0*1.0;printf(周长
3、=%f,面积=%fn,l,s);第 12 章 编 译 预 处 理 说明:(1)宏名一般习惯用大写字母表示,以便与变量名相区别。当然可以使用小写字母。(2)宏定义是用宏名简单替换宏体,也就是作简单的置换,不作语法检查,出现错误也不会报告,只有在宏展开后编译时才会报告错误。(3)程序中双引号中与宏名相同的内容不被替换。(4)宏展开后源程序将变长。第 12 章 编 译 预 处 理 (5)使用宏名,可以减少程序中重复书写某些宏体的工作量。(6)宏定义一般放在程序的开头,宏名的有效范围为定义位置到文件结束。(7)宏定义可以嵌套,后定义的宏可使用已定义的宏。第 12 章 编 译 预 处 理 12.1.2
4、带参数的宏定义带参数的宏定义 定义形式:define 宏名(参数表)宏体 带参数的宏展开时要从左至右进行参数的简单替换,使用起来较无参数宏定义复杂。例如:(1)define S(n)(n)*(n)*(n)(2)define T(n)1/(n)第一个式子代表n的立方,第二个式子代表n的倒数。第 12 章 编 译 预 处 理 说明:(1)列出的参数必须在宏体中用到。(2)带参数的宏展开只是将实参简单替换形参。如有宏定义:define S(n)n*n*n 若将形参n用实参a+b 替换,S(a+b)会变成a+b*a+b*a+b,显然与原意不相符。如将宏定义改成:#define S(n)(n)*(n)*
5、(n)S(a+b)才被替换为需要的(a+b)*(a+b)*(a+b)。因此,在带参的宏定义中,参数一般应加括号来描述。第 12 章 编 译 预 处 理 (3)宏定义时,在宏名与带参数的括号之间不应加空格,否则将空格以后的部分都作为宏体。例如:define S(n)(n)*(n)*(n)S被认为是不带参数的宏名,它代表“(n)(n)*(n)*(n)”这样的宏体。(4)如在宏体中的参数前加上“”,则在宏展开后该实参前后会加上双引号,变成字符串。第 12 章 编 译 预 处 理 例例 12 2 利用带参数的宏定义求圆的周长和面积/*程序12-2,利用带参数的宏定义求圆的周长和面积*/define R
6、 1.0define PI 3.14159define C(r)2*PI*(r)define S(r)PI*(r)*(r)main()printf(周长=%f,面积=%fn,C(R),S(R);第 12 章 编 译 预 处 理 上面程序宏展开后如下:main()printf(周长=%f,面积=%fn,2*3.14159*1.0,3.14159*1.0*1.0);带参数的宏又称为函数宏,函数宏不是函数,这是因为:(1)函数调用要求形参和实参类型一致,如实参是表达式,则必须先计算出值;宏名无类型,宏体也无类型,宏展开只进行参数的简单替换。第 12 章 编 译 预 处 理 (2)函数调用是在程序运行
7、时处理的,分配临时的内存单元,有返回值;而宏展开是在编译前进行的,展开时并不分配内存单元,不进行值的传递,无返回值。(3)宏展开将使源程序变长;而函数调用不增加源程序的长度。(4)宏替换不占用运行时间,只占用编译时间;而函数调用则占用运行时间(分配单元、保留现场、值传递、返回)。(5)一般用宏代表简单的表达式比较合适。有些问题用宏和函数都可以解决。第 12 章 编 译 预 处 理 12.1.3 预定义宏预定义宏 预定义宏由系统提供,宏名的开始和结尾均为下划线。_TURBOC_:当前TURBOC的版本号。_LINE_:源程序行号,第一行定义为1。_FILE_:源程序文件名。_DATE_:当前编译
8、日期。_TIME_:当前编译时间。第 12 章 编 译 预 处 理 12.1.4 取消宏定义取消宏定义 形式一:undef 宏名 取消前面定义的宏名,使宏名局部化,取消以后不能再使用。第 12 章 编 译 预 处 理 12.2 文件包含文件包含 文件包含是指一个C语言源程序中将另一个C语言源程序包含进来,通过include预处理指令实现。一般形式:include被包含文件名或 include 第 12 章 编 译 预 处 理 (1)被包含的文件一般指定为头文件(*.h),也可为C程序等文件。(2)一个include指令只能指定一个被包含文件,如果要包含n个文件,则要用到n条include指令。
9、(3)不能包含OBJ文件。文件包含是在编译前进行处理,不是在连接时进行处理。作用:将指定文件包含在当前文件中或插入至文件包含指令的相应位置处。使用文件包含指令,可以减少程序设计人员的重复劳动,提高程序的开发效率。第 12 章 编 译 预 处 理 (4)当文件名用双引号括起来时,系统先在当前目录中寻找包含的文件,若找不到,再在系统指定的标准方式检索其它目录。而用尖括号时,系统直接按指定的标准方式检索。一般系统提供的头文件,用尖括号。自定义的文件,用双引号。(5)被包含文件与当前文件,在预编译后变成同一个文件,而非两个文件。(6)文件包含可以嵌套,但必须按顺序包含。第 12 章 编 译 预 处 理
10、 12.3 条件编译条件编译 1.ifdef 标识符 程序段1 else 程序段2 endif 第 12 章 编 译 预 处 理 若标识符已经被定义过,则对程序段1进行编译,否则对程序段2进行编译。和if-else语句一样,#else子句可以缺省,缺省后的形式为:#ifdef 标识符 程序段1#endif还可增加#elif子句构成嵌套。第 12 章 编 译 预 处 理 (2)#ifndef 标识符 程序段1#else 程序段2#endif 与第一种形式正好相反,若标识符未被定义过,则对程序段1进行编译,否则对程序段2进行编译。第 12 章 编 译 预 处 理 3.if 表达式表达式 程序段1
11、else 程序段2 endif 当表达式之值为真(非零)时,编译程序段1,否则编译程序段2。例如:if DEBUG=1 调试代码 endif 第 12 章 编 译 预 处 理 例例 12 3 根据给定的条件编译,使给定的字符串以小写字母或大写字母形式输出。/*程序12 3,条件编译实例*/define LETTER 1 main()int i=0;char *str=HuNanComputer;char c;while(c=stri)!=0)第 12 章 编 译 预 处 理 if LETTER if(c=a&c=A&c=Z)c+=32;endif;printf(c%,c);运行结果:HUNAN
12、COMPUTER 如将LETTER定义为0,将编译另一条if语句,运行结果将变为:hunancomputer。第 12 章 编 译 预 处 理 例例 12 4 假设某程序使用与国别有关的信息,每个国家的信息存于一头文件中,采用条件编译识别与国别有关的信息。if CHINA=1include china.helif USA=1include usa.helif ENGLAND=1include england.helif FRANCE=1include france.helse include italy.hendif 第 12 章 编 译 预 处 理 12.4 图图 形形 处处 理理 12.4
13、.1 图形处理流程图形处理流程 最简单的图形处理可以借用文本方式完成,而真正的图形处理要进入图形工作方式。图形处理的第一步是初始化,可通过调用initgraph()函数完成;初始化后就可调用相应的图形处理函数绘图;处理完成调用closegraph()函数关闭图形模式,退出图形处理。第 12 章 编 译 预 处 理 初始化函数原型为:void initgraph(int*gdriver,int*gmode,char*pdrive);(1)参数gdriver:图形驱动程序的地址值。*gdriver取值为010,与对应的显卡关系为:0DETECT(自动检测)、1CGA、3EGA、9VGA、,一般选0
14、即可。(2)参数gmode:图形处理模式。*gmode取值为05,具体取值与显卡有关。在VGA显卡下:0 640200像素、1640350像素、2640480像素、,详细情况请参考相关手册。第 12 章 编 译 预 处 理 (3)参数pdrive:图形驱动程序BGI文件的路径。初始化成功后,函数int graphresult()返回0。图形处理完毕后,需调用函数closegraph()关闭图形模式。第 12 章 编 译 预 处 理 2.4.2 图形处理函数图形处理函数 图形处理函数包含在头文件graphics.h中。这里介绍几个基本处理函数,更多的图形处理函数可参见附录C。(1)颜色设置函数:
15、void setcolor(int color);(2)背景颜色设置函数:void setbkcolor(int color);color取值与颜色的主要关系为:0BLACK、1BLUE、2GREEN、4RED、14YELLOW、15WHITE。第 12 章 编 译 预 处 理 (3)获取当前x坐标:int getx();(4)获取当前y坐标:int gety();(5)画线函数:void line(int x1,int y1,int x2,int y2);其中:(x1,y1)与(x2,y2)为要画线的端点。第 12 章 编 译 预 处 理 (6)画圆函数:void cricle(int x,
16、int y,int r);其中:(x,y)为要画圆的圆心,r为要画圆的半径。(7)画弧函数:void arc(int x,int y,int a1,int a2,int r);其中:(x,y)为要画弧的中心,a1、a2分别为弧的开始角和终止角。第 12 章 编 译 预 处 理 例例12-5 在屏幕上画10条不同颜色的线和10个不同颜色的圆。程序如下:/*例12-5,画线和圆*/#include graphics.hmain()int i,x,y,r;int gdriver=DETECT,gmode;/*初始化*/initgraph(&gdriver,&gmode,C:TCBGI);/*画10条
17、不同颜色的线*/for(i=1;i=10;i+)第 12 章 编 译 预 处 理 setcolor(i);line(10*i,i,400,400);/*画10个不同颜色的圆*/for(i=1;i=10;i+)setcolor(i);cricle(100*i,100*i,100);第 12 章 编 译 预 处 理 习习 题题 十十 二二 1写出下面的程序经宏展开后的源程序,并写出程序的运行结果。#define PR printf(sum=%dn,sum)#define ADD sum+=imain()int i,sum=0;for(i=10;i20;i+)ADD;PR;第 12 章 编 译 预
18、处 理 2定义一个宏,用于判断某年是否为闰年。3定义一个宏,求一元二次方程根的判别式的值。4根据个人习惯,定义一组自己常用的输入/输出格式宏,并存入一个文件中,然后设计一程序加以验证。5用宏定义方式说明数组的类型及大小,编写输出a0至a7数组元素值的程序。假设a0=C、a1=H、a2=A、a3=N、a4=G、a5=S、a6=H、a7=A。6编写计算球体体积的程序,用宏定义方式说明圆周率PI以及计算球体体积的公式是(4/3)r3。第 12 章 编 译 预 处 理 7编写程序,求两个整数相除的余数。用带参数的宏来实现。8编写程序,分别用函数和带参数的宏求三个数的最大值。9编写程序,利用带参数的宏交
19、换两个数。第 12 章 编 译 预 处 理 10分析下面程序的执行结果。/*文件名t_10.c*/#include t_11.cfun1()printf(Visual BASICn);fun2();/*文件名t_11.c*/fun2()printf(Turbo Cn);/*文件名t_12.c*/#include t_10.c main()printf(Delphin);fun1();第 12 章 编 译 预 处 理 11输入一串字符,可以选择原文输出或密码(原字符的后继)输出。用条件编译控制是否要译成密码。12举例说明如何利用条件编译预处理指令来调试程序。13编写程序,借用文本方式在屏幕上画一个1110大小的棋盘。14编写程序,在屏幕上随机画10段不同颜色的弧。15编写程序,在屏幕上画一个变化的环,方法是将半径为r的圆周等分n等份,再以每个等分点为圆心、以r1为半径画n个圆,r、r1及环的颜色用命令行参数的形式给出。