第6章 编译预处理.ppt_第1页
第6章 编译预处理.ppt_第2页
第6章 编译预处理.ppt_第3页
第6章 编译预处理.ppt_第4页
第6章 编译预处理.ppt_第5页
已阅读5页,还剩22页未读 继续免费阅读

下载本文档

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

文档简介

1、本章要求:,掌握无参数宏和带有参数宏定义和使用方法; 掌握文件包含的使用方法; 掌握条件编译的使用。,本章难点: 带有参数宏定义和使用方法,本章重点: 无参数宏和带有参数宏定义和使用方法,第6章 编译预处理,第6章 编译预处理,所谓编译预处理,指的是在源程序文件中,加入“编译预处理命令”,使编译程序在对源程序进行通常的编译之前,先对这些特殊的命令进行“预处理”,然后将预处理的结果和源程序一起再进行通常的编译处理,以得到目标代码(OBJ文件)。 6.1 宏定义 #define 6.2 文件包含命令 #include 6.3条件编译命令,在语言源程序中允许用一个标识符来表示一个字符串,称为“宏”。

2、被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。 宏是由源程序中的宏定义命令# define来定义的。宏代换是由预处理程序自动完成的。 在语言中,“宏”分为有参数和无参数2种。,6.1 宏定义 #define,6.1 宏定义 #define,6.1.1 无参宏定义 一般形式: #define 标识符 字符串 它的作用是用一个指定的标识符来代表一个字符串。常使用宏定义命令 #define 定义符号常量,若有如下定义: #define PI 3.1415926535 即用标识符(称为“宏名”)PI来代表字符串

3、“3.1415926535”。 在编译预处理中,即以3.1415926535代替源程序中出现的PI (这个过程称为“宏展开”)。如程序中的s=PI*r*r等效于s=3.1415926535*r*r。,6.1.1 无参宏定义,说明: (1)标识符(宏名)命名,遵守C语言标识符的命名规则。为便于与一般变量区别,宏名常采用大写字母。 (2)宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时由预处理程序层层代换。如例6-1中有定义: #define PI 3.1415926 #define S PI*r*r /* PI是已定义的宏名*/ 在编译预处理时,语句printf(“The A

4、rea=%lfn”,S); 经宏代换后变为: printf(The Area=%lfn,3.1415926*r*r); 注意:若替换的字符串中含有其它字符(如上题中的r),一般要在引用宏的程序需要定义,否则编译时会出现标识符(变量)未定义的错误。,6.1.1 无参宏定义,(3)宏定义是用宏名代表字符串,仅作简单置换,不分配内存空间,编译预处理时不检查语法。只有在源程序被宏展开后,进行编译时才作语法检查。 下列的宏定义都不可取: #define PI =3.1415926535 /* 多了等号*/ #define PI 3.1415926535; /* 多了分号*/ 上述宏定义对于同一个语句:

5、s=PI*r*r; 宏展开后分别为: s=3.1415926535*r*r; s=3.1415926535; *r*r; 显然有语法错误!,6.1.1 无参宏定义,(4)宏替换只对单独的宏名单词进行,对于括在引号中的字符串不起作用。若有宏定义:#define MAX 500 对于下列语句: int MAXICOAT=10; /* MAX不是独立单词,不替换*/ printf(“MAX=”, MAX); /*前面的MAX在引号中,不替换;后面的要替换*/ (5)宏定义必须写在函数之外,宏名的有效范围为定义命令之后到该源文件结束,也可以使用 #undef 命令来提前终止宏名的作用域。 #defin

6、e MAX 500 宏名MAX的有效范围 #undef MAX 宏名MAX在此处无效,6.1.1 无参宏定义,有效地利用宏定义可以简化程序,如对输出格式的宏定义,看下面的例6-2 #define PR printf #define NL n #define D %d #define F %f #define S %s void main() int a=5,b=8; float x=4.3,y=2.7108; char ch=You are good!; PR(D F NL,a,x); /*即为 printf(%d %f n, a,x); */ PR(D D F F NL,a,b,x,y);

7、PR(S NL,ch); ,程序的运行结果为: 5 4.300000 5 8 4.300000 2.710800 You are good!,6.1 宏定义 #define,6.1.2 有参宏定义 语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。 带参宏定义的一般形式为: #define 宏名(形参表) 字符串 带参宏调用的一般形式为: 宏名(实参表); 宏定义:#define MAX(x,y) xy?x:y 宏调用:z= MAX(3,4); 宏展开:z=34?3:4;,6.1.2 有参宏定义,例6-

8、3 用定义带参数宏来改写例6-1的程序,输入一个圆的半径,输出圆的面积。定义带参数的宏S(r),程序如下 #define PI 3.1415926535 #define S(r) PI*r*r void main() float a, area; a = 5; area = S(a); /*宏展开为area=3.1415926535*a*a;*/ printf(r=%fnarea=%fn,a,area); ,这样可以解决,无参数宏定义中,替换字符串中含有其他字符,编译时出现标识符(变量)未定义错误的情况。,6.1.2 有参宏定义,说明: (1)带参数的宏展开时,是用实参字符串替换形参字符串,由

9、于运算符的优先级问题,可能发生的逻辑错误。比较好的办法是宏定义的形参加括号。对比下面两个例子: 例一,宏定义:#define S(r) 3.14*r*r 宏调用语句:area = S(a+b); 宏展开后:area =3.14*a+b*a+b; 例二,宏定义: #define S(r) 3.14*(r)*(r) 宏调用语句:area = S(a+b); 宏展开后:area =3.14*(a+b)*(a+b),6.1.2 有参宏定义,(2)宏定义时,宏名与参数表间不能有空格,否则将作为无参数宏来处理。如有定义:#define MAX (x,y) xy?x:y 则认为MAX为宏名,而(x,y) x

10、y?x:y为替代字符串,则易产生错误。 (3)带参数的宏和函数有相似之处,它们都有形参实参的概念,并要求形参实参的数目相同,一一对应。但两者实现的过程是不同的。 在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义。而宏调用中的实参有具体的值。要用它们去代换形参,因此必须作类型说明。这是与函数中的情况不同的。在函数中,形参和实参是两个不同的量,各有自己的作用域,调用时要把实参值赋予形参,进行“值传递”。而在带参宏中,只是符号代换,不存在值传递的问题。,6.1.2 有参宏定义,下面通过例6-4与例6-5来说明它们的区别。,6.1.2 有参宏定义,函数调用和宏调用二者在形式上相似,在本质上是

11、完全不同的。现将它们的区别归纳如表6-1所示:,表6-1 带参数的宏和函数,6.1.2 有参宏定义,(4)宏定义中,若替换字符串中的形式参数在引号中,则宏展开时不被实参替换。但如果在替换字符串中,形式参数以#作为前缀,那么宏展开时它将被带引号的实参字符串替换,如: #define MYPRINT1(exp) printf(exp=%dn,exp) #define MYPRINT2(exp) printf(#exp=%dn,exp) void main() MYPRINT1(2+3);/*宏展开 print(exp=%dn,2+3); */ MYPRINT2(2+3);/*宏展开 print(2

12、+3 =%dn,2+3); */ /*其中字符串被连接起来,即print(2+3=%dn,2+3); */ 运行结果如下:exp=5 2+3=5,6.2 文件包含命令 #include,所谓“文件包含”,是指一个原文件可以将另外一个原文件的所有内容包含进来。其使用格式为: # include “文件名” 或:# include 例如,在前面我们已多次用此命令包含过库函数的头文件: #include “stdio.h” /* 包含标准输入输出头文件*/ #include “math.h” /* 包含数学函数头文件*/ #include “string.h” /* 包含字符串处理函数头文件*/ 文

13、件包含命令的功能是在预处理时,把“文件名”指定的文件内容复制到本文件(即是用指定文件的内容去替换# include命令行),再对合并后的文件进行编译。,6.2 文件包含命令 #include,main() abc(); , int abc() xyz();. ,int xyz() . ,File1.c,File2.c,File3.c,6.2 文件包含命令 #include,若例6-6 改写例6-3为两个文件。 文件一:文件名为Area.h;文件内容如下: #define PI 3.1415926535 #define S(r) PI*r*r 文件二:文件名为myfile.c;文件内容如下: #

14、include Area.h /*在预处理时,此处替换为文件Area.h的内容*/ void main() float a, area; a = 5; area = S(a); printf(r=%fnarea=%fn,a,area); ,6.2 文件包含命令 #include,说明: (1)#include命令中,文件名可以用双引号括起来,也可以用尖括号括起来。例如以下写法都是允许的:#includestdio.h #include 如果文件名是用引号括起来的,那么就先在源程序所在的位置查找该文件,若找不到,再到存放C库函数头文件所在的目录中去查找;如果文件名是用尖括号“”括起来的,那么直接

15、到存放C库函数头文件所在的目录中去查找。,6.2 文件包含命令 #include,(2)一个#include命令只能包含一个文件,如果要包含多个文件,要用多个#include命令。当然,被包含的文件本身也可以使用#include命令去包含别的文件,即使用嵌套的文件包含。,6.2 文件包含命令 #include,(3)从理论上说,#include命令可以包含任何类型的文件,只要这些文件的内容被扩展后符合C语言语法。一般#include命令用于包含扩展名为.h的“头文件”,如stdio.h、string.h、math.h。在这些文件中,一般定义符号常量、宏,或声明函数原型。 (4)被包含的文件与其

16、所在的文件,在预处理后,成为一个文件,因此,如果被包含文件定义有全局变量,在其所在文件中不必用extern关键字声明。但一般不在被包含文件中定义变量。,6.3 条件编译命令,预处理程序提供了条件编译的功能。可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。 条件编译有三种形式,本节分别介绍:,1第一种形式: #ifdef 标识符 程序段1 #else 程序段2 #endif,2第二种形式: #ifndef 标识符 程序段1 #else 程序段2 #endif,3第三种形式: #if 表达式 程序段1 #else 程序段2 #endif,6.3 条件编译命令,功能说明: 1第一种

17、形式:如果标识符已被#define命令定义过则对程序段1进行编译;否则对程序段2进行编译。如果没有程序段2(它为空),本格式中的#else可以没有. 2第二种形式:如果标识符未被#define命令定义过则对程序段1进行编译,否则对程序段2进行编译。这与第一种形式的功能正相反。 3第三种形式:表达式为常量整型表达式(其中不能包含sizeof、强制类型转换运算符或枚举常量),如表达式的值为真(非0),则对程序段1 进行编译,否则对程序段2进行编译。,6.3 条件编译命令,例6-7 根据是否定义VOL宏,决定是计算球的体积还是表面积: #define PI 3.1415926 #define VOL(r) 4.0/3*PI*(r)*(r)*(r) void main() double r,v,s; printf(Enter The Radiusn); scanf(%lf, #endif ,程序中,如果没第2行的宏定义,系统编译求球体表面积的那一段程序,而计算体积的那段程序就不编译。 读者思考,如果不用条件编译,而改用if条件语句来控件是可也可以做到。,本章小结,(1)预处理功能是语言特有的功能,它是在对源程序正式编译前由预处理程序完成的。使用预处理功能便于程序的修改、阅读、移植和调试,也便

温馨提示

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

评论

0/150

提交评论