1、第六章 预处理程序,的源程序在进行编译之前要经过“预处理”。 预处理程序主要完成以下三种功能:1. 宏替换2. 文件包含3. 条件编译,6.1 宏替换, 例如: double area(radius) double radius; return(3.1415926536*radius*radius); ,#define PI 3.1415926536 double area(radius) double radius; return(PI*radius*radius); 其中: 1. 由“#”开始的命令是预处理程序处理的命令。 2. #define是宏定义命令,其一般形式为:#define s
2、tring1 string2 表示要把源代码中出现在该行之后的每一个string1都用string2来代替。,6.1.1 简单宏替换,说明: 1. 宏定义必须写在第一次使用该宏定义的代码之前; 2. 宏定义不是以分号结束的; 3. #define、string1、string2之间至少要有一个空格; 4. string1称为宏,string2称为宏扩展例如:PI是宏,3.1415926536是宏PI的宏扩展 5. 宏名用大写字母来表示是一个传统的习惯; 6. 使用宏定义的好处:1)简化程序的书写 2)提高程序的可读性3)便于程序的修改 4)提高程序的可移植性,6.1.1 简单宏替换,例如: #
3、define PI 3.14 double area(r) double r; return(PI*r*r); 当要提高的精度时,可以将宏定义 #define PI 3.14 改为 #define PI 3.1415926536,6.1.1 简单宏替换,又如不同操作系统的文件结束符是不同的,有的是-1,有的是用其它字符。为此,我们用如下的宏定义: #define EOF -1 定义一个宏EOF。当系统环境改变时只需重新定义一下EOF即可方便地将程序移植到新环境上。 7. 预处理程序将不替换字符串中或用单引号括起来的字符常量的“宏”。,6.1.1 简单宏替换,例如: #define HELLO
4、“bonjourn” #define A B main() printf(HELLO);printf(“%cn”, A); ,bonjour B,6.1.1 简单宏替换,#define HELLO bonjour #define A B main() printf(“HELLOn”);printf(“%cn”,A); ,HELLO A,6.1.1 简单宏替换,8.前面定义的宏名,则在后面的#define中可以被引用。 例如: #define PI 3.1415926536 #define PI SQUARE PI*PI,6.1.1 简单宏替换,6.1.2 带有参数的宏替换,#define 宏名
5、(参数表) 字符串其中:字符串中要包含参数表中指定的参数 例如:要在程序中计算大小不同的圆的面积则可定义如下的带有参数的宏: #define area(x) (3.1415926536*x*x) main() printf(“%fn”,area(2.5); ,经预处理程序(cpp.exe)宏替换之后,将变为: main() printf(“%fn”,(3.1415926536*2.5*2.5); 其中: area(2.5)称为宏调用,其形式为:宏名(实参表) 注意:1.宏定义中的“宏名”和“(参数表)”之间不能有空格。如:#define area(x) (3.1415926536*x*x) 不
6、能写成 #define area (x) (3.1415926536*x*x),6.1.2 带有参数的宏替换,#define area (x) (3.1415926536*x*x) main() printf(%fn,area(2.5); cpp test.c type test.i main() printf(“%fn”,(x)(3.1415926536*x*x)(2.5); ,6.1.2 带有参数的宏替换,2.在定义带有参数的宏替换时,要用圆括号将宏扩展部分括起来。如: #define square(n) (n*n) main() printf(“%fn”,27.0/square(3.0)
7、; ,3.000000,6.1.2 带有参数的宏替换,#define square(n) n*n main() printf(%fn,27.0/square(3.0); type test.i main() printf(%fn,27.0/3.0*3.0); ,27.000000,6.1.2 带有参数的宏替换,3. 带参数的宏调用和函数调用是完全不同的.宏调用是在编译之前完成的函数调用是在编译之后实现的。例: main() int i=1;while(i=10)printf(“%dn”,square(i+); square(n) int n; return(n*n); ,1 4 9 16 25
8、 36 49 64 81 100,6.1.2 带有参数的宏替换,将函数调用改成带有参数的宏调用。 #define square(n) (n*n) main() int i=1;while(i=10)printf(“%dn”,square(i+); type test.i main() int i=1;while(i=10)printf(“%dn”,(i+*i+); ,2 12 30 56 90,6.1.2 带有参数的宏替换,#define square(n) (n*n) main() int i=1;printf(“%dn”,square(i+4); 最好把每个形参也用圆括号括起来。如:#de
9、fine square(n) (n)*(n) 则经宏扩展后变为: (i+4)*(i+4),6.1.2 带有参数的宏替换,取消已有的宏定义命令: #undef 宏名 作用:在#undef行后面出现的“宏名”将不被扩展。 例如: #define P 3.14 main() float r=2.0;printf(p*r*r);#undef p int p=10;printf(“%dn”,p);,6.1.2 带有参数的宏替换,6.2 文件包含,文件包含: 是指一个源文件可以将另外一个源文件的全部内容包含进来。其一般形式为: #include “文件名”#include 文件名 功能: 把指定文件的内容
10、插入到该#include命令所在之处。其中: “文件名”:首先在当前目录中寻找文件。如果找不到,再到一系列系统预先设定的目录中去找。 文件名:则不在当前目录中寻找,而是径直到系统预先设定的目录中去寻找该文件。,例如:f1.c f2.c f1.c,B,(1)文件print. h (2)文件file1.c#define PR printf #include “print. h”#define NL “n” main()#define D “%d” int a=1,b=2,c=3;#define D1 D NL PR(D1,a);#define D2 D D NL PR(D2,a,b);#defin
11、e D3 D D D NL PR(D3,a,b,c); ,文件file1.i main() int a=1,b=2,c=3; printf (“%d“ “n“ ,a); printf (“%d“ “%d“ “n“ ,a,b); printf (“%d“ “%d“ “%d“ “n“ ,a,b,c); ,运行结果: 1 1 21 2 3,例如文件defs.h #define BUFSIZE 128 #define EOF -1 #define FALSE 0 #define TRUE 1 #define NULL 0 #define BELL 7 #define ESC 27 #define BL
12、ANK 32,说明: 1.由于#include命令常出现在源文件的头部。所以我们也称被包含进来的文件为“头文件”。 2.编译系统本身也提供有许多这样的头文件。如:stdio.h、string.h、math .h 3.一个#include命令只能指定一个被包含的文件。 4.#include也可以嵌套。但不能递归包含。,6.3 条件编译 ,条件编译:有时希望程序其中的一部分只有在满足一定条件时才进行编译,否则不参与编译。 条件编译有以下几种形式: 1.#if 常量表达式程序段#endif 作用:如果常量表达式为真(非零),则相应的程序段被编译。否则跳过它。如:,#define MAX 100 ma
13、in() #if MAX99printf(array length 99n);#endif 注意:常量表达式是在编译时求值的。因此它只能由事先定义的宏名和常量组成,而不能出现变量。,2. #if 常量表达式程序段1#else程序段2#endif 例如:#define ENGLISH 1main() #if ENGLISHprintf(HELLO!n);#elseprintf(你好!n);#endif,作用:如果常量表达 式为真,则编译程序 段1,否则编译程序 段2。,3.#if 常量表达式1程序段1#elif 常量表达式2程序段2#elif 常量表达式n程序段n#else程序段n+1#endi
14、f ,4. #ifdef 宏名程序段#endif 作用:如果已定义了相应的“宏名”,则编译相应的程序段;否则跳过它。如: #define ESC 27 #ifdef ESC#undef ESC #endif,5. #ifdef 宏名程序段1#else程序段2#endif 作用:如果定义了相应的“宏名”,则编译程序段1;否则,编译程序段2。,6. #ifndef 宏名程序段#endif 作用:如果相应的“宏名”没有定义,则编译相应的程序段。否则跳过它。如:#ifndef PI#define PI 3.1415926536#endif,7. #ifndef 宏名程序段1#else程序段2#endif 作用:如果没有定义相应的“宏名”,则编译程序段1。否则,编译程序段2。,