1
第九章 预编译处理主讲 福州大学数学与计算机学院 韩晓芸
E-mail,hxy@fjtv.net
2
第九章 编译预处理
第一节 编译预处理 概述
第二节 宏定义
第三节 文件包含
第四节 条件编译
3
第一节 编译预处理概述什么是 C预处理程序预处理命令
4
在用 C编译程序对 C源代码进行编译之前,
即在语法分析,代码生成和优化之前,由 C
预处理程序对源代码进行第一次处理 。 处理时,它忽略注释语句,加入,h头文件,并按定义进行替换 。 预处理的输出,即是编译程序的输入 。
什么是 C预处理程序第一节 编译预处理概述预处理的过程
5
源文件
pro.c 预处理程序 头文件stdio.h
编译程序目标文件
pro.obj
连接程序可执行程序
pro.exe
目标文件
misc.obj

slibce.lib
预处理过程
6
预处理命令共有十二条,分为五类,
– 宏定义
– 文件包含
– 条件编译
– 行号和文件名控制
– 其它这里介绍 宏定义、文件包含和条件编译 。为了与一般 C语句相区别,这些命令以符号,#”开头。
第一节 编译预处理概述预处理命令
7
不带参数的宏定义带参数的宏定义宏与函数取消宏定义第二节 宏定义
8
不带参数的宏定义的一般形式为:
#define 标识符 字符串用一个指定的标识符(即名字)来代表一个字符串,也是定义符号常量的形式,
如见过的:
#define PI 3.14159
#define TAX_RATE 0.0825
不带参数的宏定义第二节 宏定义
9
例 1:
#define PI 3.1415926
main()
{float l,s,r,v;
printf("input radius:");
scanf("%f",&r);
l=2.0*PI*r;
s=PI*r*r;
v=4.0/3*PI*r*r*r;
printf("l=%10.4f\ns=%10.4f\nv=%10.4f\n",l,s,v);
}
不带参数的宏定义第二节 宏定义运行情况如下:
input radius,4
l=25.1327
s=50.2655
v=268.0826
10
说明:
1,宏名一般习惯用 大写 字母,以便与变量名区别
2,使用宏名代替一个字符串,可以减少重复书写某些字符串的工作量
3,宏定义 只做简单的置换,不作正确性检查
4,宏定义不是 C语句,不必在行末加分号
5,#define命令出现在程序中函数的外面,其有效范围 从定义命令之后到本源文件结束
6,可以用 #undef命令 终止宏定义的作用域不带参数的宏定义第二节 宏定义
11
说明:
7,在进行宏定义时,可以引用已定义的宏名,可以 层层置换 。如 例 2:
#define R 3.0
#define PI 3.1415926
#define L 2*PI*R
#define S PI*R*R
main()
{
printf("L=%f\nS=%f\n",L,S);
}
不带参数的宏定义第二节 宏定义运行情况如下:
l=18.849556
s=28.274333
12
说明:
8,程序中双引号引起来的字符串内的字符,即使与宏名相同,也不进行置换。(如例 2中的
printf语句。 printf("L=%f\nS=%f\n",L,S);
9,宏定义是专门用于预处理命令的一个专有名词,
它与定义变量的含义不同,只作字符替换,不分配内存空间。
不带参数的宏定义第二节 宏定义
13
带参数的宏定义的一般形式:
#define 宏名 (参数表 ) 字符串带参数的宏定义
#define S(a,b) a*b
area=S(3,4);
宏名与带参数的括号之间不能加空格 。

得 12
注意!
第二节 宏定义如,
14
例 3:
#define PI 3.1415926
#define S(r) PI*r*r
main()
{float a,area;
a=3.6;
area=S(a);
printf("r=%f\narea=%f\n",a,area);
}
运行情况如下:
r=3.600000
area=40.715038
带参数的宏定义第二节 宏定义
15
例 4,编写一个求平方的宏。
#include "stdio.h "
#define SQR(n) ((n)*(n))
main( )
{int i=5,j;
j=SQR(i+5);
printf(" The square of %d is %d\n ",i,j);
}

结果?
将宏定义语句改为 #define
SQR(n) (n*n)结果如何?
带参数的宏定义第二节 宏定义
16
带参宏定义与函数的区别:
1,函数调用时,先求实参表达式的值,然后代入形参。 而 带参的宏只进行简单的字符替换 。
2,函数调用是在 程序运行时处理的,分配临时内存单元。 宏展开则是在 编译时进行的,且不分配内存单元,不进行值的传递处理。
3,函数中的 实参与形参都要定义类型,且必须类型一致。而宏不存在类型问题,宏名无类型,
它的参数也无类型,只是一个符号代表。
4,调用 函数只可得到一个返回值,而用 宏可以设法得到几个结果 。
带参数的宏定义第二节 宏定义
17
例 5:
#define PI 3.1415926
#define CIRCLE(R,L,S,V)
L=2*PI*R;S=PI*R*R;V=4.0/3.0*PI*R*R*R
main()
{float l,s,r,v;
printf("input radius:");
scanf("%f",&r);
CIRCLE(r,l,s,v);
printf("r=%6.2f\nl=%6.2f\ns=%6.2f\nv=%6.2f\n",r,l,s,v);
}
运行结果:
r= 3.50
l= 21.99
s= 38.48
v=179.59
带参数的宏定义第二节 宏定义
18
带参宏定义与函数的区别:
5,宏替换不占运行时间,只占编译时间。而函数调用占运行时间。有些问题,用宏和函数都可以,如 例 6:
带参数的宏定义第二节 宏定义
#define MAX(x,y)
(x)>(y)?(x):(y)
main()
{int a=5,b=3,c=2,d=6,t;
t=MAX(a+b,c+d);
printf("%5d\n",t);
}
int max(int x,int y)
{return(x>y?x:y);}
main()
{int a=5,b=3,c=2,d=6,t;
t=max(a+b,c+d);
printf("%5d\n",t);
}
19
善于利用宏定义,可以实现程序的简化。 如事先将程序中“输出格式”定义好,可以减少每次写出具体输出格式的麻烦。如 例 7:
#define PR printf
#define NL "\n"
#define D "%d"
#define D1 D NL
#define D2 D D NL
#define D3 D D D NL
#define D4 D D D D NL
#define S "%s"
带参数的宏定义第二节 宏定义
main()
{int a,b,c,d;
char string[]="CHINA";
a=1;b=2;c=3;d=4;
PR(NL);
PR(D1,a);
PR(D2,a,b);
PR(D3,a,b,c);
PR(D4,a,b,c,d);
PR(S,string);}
20
宏取消的一般形式为:
#undef 宏替换名
#undef用来删除事先定义了的宏定义。 例如,
#define LEN 100
#define WIDTH 200
char array[LEN][WIDTH];
#undef LEN
#undef WIDTH
取消宏定义第二节 宏定义
21
第三节 文件包含文件包含的一般形式文件包含应用文件包含分类
22
B
文件包含的一般形式为:
– #include "文件名 "
– #include <文件名 >
stdio.h file.c
#include "stdio.h"
file.c
A A
B
文件包含的一般形式第三节 文件包含
23
由几个文件构成的大型程序,其每个文件都有共同的部分(如函数、宏定义),把这些内容构成一个文件,以包含语句形式加在各个文件头 。例如文件 cons.h的内容为:
#define PI 3.14159
#define TAX_RATE 0.0825
#define CLS printf(" \033[2j ")
在每个源文件中可以有包含语句开头,
#include " cons.h"
第三节 文件包含 文件包含的应用
24
可以将前面例 7的程序改为如下。
例 8:
(1)文件 format.h
#define PR printf
#define NL "\n"
#define D "%d"
#define D1 D NL
#define D2 D D NL
#define D3 D D D NL
#define D4 D D D D NL
#define S "%s"
第三节 文件包含文件包含的应用
(2) 文件 li908.c
#include
“e:\yanshi\format.h"
main()
{int a=1,b=2,c=3,d=4;
char string[]="CHINA";
PR(NL);
PR(D1,a);
PR(D2,a,b);
PR(D3,a,b,c);
PR(D4,a,b,c,d);
PR(S,string);}
25
– ".c"文件,源文件。
– ".h "文件,标题文件或头文件。
例如:
stdio.h 标准输入输出头文件
string.h 字符串操作函数头文件
math.h 数学库函数头文件
conio.h 屏幕操作函数头文件
dos.h DOS接口函数头文件
alloc.h 动态地址分配函数头文件第三节 文件包含文件包含的分类
26
条件编译,按条件 对 C程序的一部分进行编译,
其它部分不编译。
条件编译的目的,是使源代码能更迅速、更容易地进行修改,并使目标代码缩短。这样,当程序在不同系统上编译、在同一系统不同编译器上编译或进行不同目的的编译时,减少对程序语句的修改。
第四节 条件编译
27
#ifdef语句 #ifndef语句
# ifdef的一般形式是:
# ifdef 标识符程序段 1
# else
程序段 2
# endif
# ifndef的形式为:
# ifndef 标识符程序段 1
# else
程序段 2
# endif
28
#if语句
# if的一般形式是:
# if 表达式程序段 1
# else
程序段 2
# endif
29
例 9,输入一行字母字符,根据需要设置条件编译,使之能将字母全改为大写输出,或全改为小写输出。
#define LETTER 1
main()
{char str[20]="C Language",c; int i=0;
while((c=str[i])!='\0')
{i++;
#if LETTER
if(c>='a'&&c<='z')
c=c-32;
#else
if((c>='A'&&c<='Z')
c=c+32;
#endif
printf("%c",c);}}
如果将程序第一行改为:
#define LETTER 0
运行结果有何变化?
结果?