1、第七章 编译预处理 教学时间: 2学时 教学目的: 1、掌握宏定义的概念及使用 2、理解“文件包含”的概念和使用 3、了解条件编译的概念 内容、重点、难点: 内容: 7.1 宏定义 7.2 文件包含 重点: 1、宏定义的概念 2、宏定义在程序中的应用 难点: 对条件编译的理解 注:教案尾页有小结和作业 第7章 编译预处理 编译预处理是在编译前对源程序进行的一些预加工。它是C语言与其它高级语言的一个重要区别,有助于编写易移植、易调试的程序。
2、预处理,顾名思义,就是编译器在对源程序进行编译之前对其进行宏定义的替换及文件包含的嵌入等操作。 7.1 宏定义 一、问题的提出 程序中经常遇到重复多次地书写某些字符串的情况,例如:3.1415926,工作量大,且不好记忆,能否作一个替换呢? 我们可以通过宏定义的方法来解决这一问题。 二、不带参数的宏定义 不带参数的宏定义的一般形式: #define 宏名 宏体 例如:#define PAI 3.1415926 例7.1 求半径为r的圆的面积和球的体积。 #define PAI 3.1415926 main( ) {
3、float r , s , v ; printf (″\n input r=″) ; scanf (″%f″,&r) ; s=PAI * r * r ; v=3.0/4 * r * r * r ; printf (″s=%10.4f\nv=%10.4f\n″ , s , v) ; } [分析] 进行预处理后,函数中的宏名PAI将被其对应的宏体字符串所替换 s=3.1415926 * r * r ; v=3.0/4 * 3.1415926 * r * r * r ; 关于宏定义的说明: 1. 宏名的替换过程其实是一种简单的复制工作,它不作任何的计算, 也不作任何错
4、误检查,错误的检查工作要等到编译过程中才会进行。 2. 宏名一般习惯写成大写字母,主要是为了与变量名区分。 3. 从上例中可以看出,使用宏名后可以提高程序的可读性。 4. 进行宏定义时,可以使用前面已定义的宏名 例7.2 #define R 3.0 #define PAI 3.1415926 #define L 2*PAI*R #define S PAI*R*R main() { printf(″L=%f\nS=%f\n″,L,S); } 经过宏替换后printf函数调用语句为: printf(″L=%f\n″,2*3.141592
5、6*3.0,3.1415926*3.0*3.0) 5. 在进行替换的预处理时,对程序中被“”(双引号)括起来的宏名将不进行宏替换。 三、带参数的宏定义 带参数的宏定义的一般格式形式: #define 宏名(参数表) 宏体 例如:#define JI(x,y) x*x*y m=JI(4,5); 注意:宏名与参数表的括号之间不可有空格符,否则,系统将把程序中的宏名替换成空格后的字符串,变成是一个不带参数的宏定义。 例7.3 用带参宏定义求长方体体积。 #define V(a,b,c) a*b*c main() { float a,b
6、c,z; printf(″\n input a,b,c: ″); scanf(″%f%f%f″,&a,&b,&c); z=V(a,b,c); printf(″V=%f\n″,z); } 运行结果 input a,b,c:4 5 6 V=120 说明: 1.在宏定义中的形参是标识符,而在程序中的宏调用实参可以是表达式。 2.在宏定义中,宏体内的形参通常要用括号括起来,以免出错。 3.带参的宏定义与带参的函数,它们从形式上与特征上都很相似,但它们在本质上是不同的: (1) 函数调用时是在程序的执行过程中进行的,要发生控制权的转移,而宏的
7、替换工作是在预处理中进行的,宏名被替换后,宏体将是程序代码的一部分,不发生控制权的转移。 (2) 在函数调用时,要先求表达式的值,然后将这个值赋给形式参数,而使用宏替换时只是简单地用实参字符串替换宏体中的形式参数,因此,若实参不是数值字符串,而是表达式的形式,那么它也仅将表达式原样搬过去替换宏体中的形参。 (3) 函数中的形式参数是有一定数据类型的,实参也是同一数据类型,否则要进行类型转换。在带参的宏定义中,形式参数是没有数据类型的,仅仅是一个符号,因而实参可以是任何数据类型。 (4) 使用函数时不管调用读函数多少次,程序逻辑代码的长度是不会增加的,使用宏定义时,宏替换的次数越多,该程序
8、就会变得越长。另一方面,函数的调用过程要进行控制权的转移、参数的传递、分配内存、保护现场、返回等工作,而宏替换的过程是在程序编译之前完成的。 四、宏定义的撤消 宏名的有效范围是从宏定义开始至文件的结束,若不希望其有效范围至文件结束,可以用下面的预编译指令来缩短其有效范围。 撒消宏定义的形式: #undef 宏名 此指令的功能是取消已定义过的宏名,这样一来,宏名的有效范围变为从该宏定义开始至对应#undef语句止。 例7.5 #define PAI 3.1415926 main ( ) { …… } #undef PAl 3.1415926
9、 func ( ) { …… } 则PAl只在main函数中有效,而在func函数中无效。 7.2 文件包含 一、问题的提出 模块化程序设计过程中,通常由多个程序员编写各个功能模块,这些模块形象的比喻成机器零件,如何将这些零件组装起来呢?通过文件包含可以解决这一问题。 二、文件包含 文件包含也叫宏包含,是编译预处理程序的一个重要功能,它通过命令#include把已经进入系统的另一个文件的整个内容嵌入进来。它实际上是宏替换的延伸。 方式1: #include “文件名” 文件名中包含有文件路径。按这种格式定义时,预处理程序首先在原来的源文件所在目录中检索该指定的文件;如果没有找到,则按系统指定的标准方式检索其他文件目录,直至找到为止。 方式2: #include <文件名> 按这种格式定义时,预处理程序只按系统规定的标准方式检索文件目录。 在前面我们已多次使用过#include命令包含过库函数的头文件,如 #include ″stdio.h″ #include ″math.h″ 本章小结 编译预处理这一章的内容较为重要,要编写移植性好且易于调试的C语言程序经常要用到这部分内容。本章只介绍一些最基本的概念。 作业: P138 上机指导二 1 2






