第 8章 预处理命令
,C语言程序设计,
课程讲义
2006年 4月
上一章节课程回顾
函数定义的一般形式
函数的参数和函数的值
函数的调用
函数的嵌套调用
函数的递归调用
数组作为函数参数
局部变量和全局变量
变量的存储类别
第 8章 预处理命令
8.1 概述
8.2 宏定义
8.3 文件包含
8.4 条件编译
8.1概述
编译预处理,在源程序文件中,加入, 编译预处理命
令,,使编译程序在对源程序进行通常的编译(包
括词法分析、语法分析、代码生成、代码优化)之
前,先对这些命令进行预处理,然后将预处理的结
果和源程序一起再进行通常的编译处理,以得到目
标代码( OBJ文件)。
C提供的编译预处理命令
宏命令( Macro)
文件包含命令( include)
条件编译命令
这些命令均以 #开头,以区别于语句。
8.2 宏( Macro)定义
一、不带参数的宏
一般形式,#define 标识符 字符串
如,#define PI 3.1415926
作用:用标识符(称为, 宏名, ) PI代替字符串
,3.1415926”。
在预编译时,将源程序中出现的宏名 PI替换为字符串
,3.1415926”,这一替换过程称为, 宏展开, 。
#define:宏定义命令
#undef:终止宏定义命令
[例 ]
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.0*PI*r*r*r; /* 球体积 */
printf("l=%10.4f\ns=%10.4f\nv=%10.4f\n",l,s,v);
}
关于宏定义的说明,
1、一般宏名用大写字母表示。(变量名一般用小写字母)。
2、使用宏可以提高程序的可读性和可移植性。如上述程序中,多处
需要使用 π 值,用宏名既便于修改又意义明确。
3、宏定义是用宏名代替 字符串,宏扩展时仅作简单替换,不检查语
法。语法检查在编译时进行。
4、宏定义不是 C语句,后面不能有分号。如果加入分号,则连分号
一起替换。
如,
#define PI 3.1415926;
area = P*r*r;
在宏扩展后成为,
area = 3.1315926; *r*r;
结果,在编译时出现语法错误。
5、通常把 #define命令放在一个文件的开头,使其在本文件
全部有效。( #define定义的宏仅在本文件有效,在其它
文件中无效,这与全局变量不同)。
6、宏定义终止命令 #undef结束先前定义的宏名。
#define G 8.8
main()
{
}
#undef G /* 取消 G的意义 */
f1()

7、宏定义中可以引用已定义的宏名。
[例 8.2]
#define R 3.0
#define PI 3.1415926
#deinfe L 2*PI*R
#define S PI*R*R
main()
{
printf("L=%f\nS=%f\n",L,S);
}
8、对程序中用双引号括起来的字符串,即使与宏名相
同,也不替换。例如上例的 printf语句中,双引号括
起来 L和 S不被替换。
二、带参数的宏
一般形式,
#define 宏名(参数表) 字符串
带参数的宏在展开时,不是进行简单的字符串替换,而是进行参数
替换。例,
说明,
带参数的宏展开时,用实参字符串替换形参字符
串,注意可能发生的错误。比较好的办法是宏定义
的形参加括号。
[例 ]
#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);
}
[例 ] 返回多个值的宏定义。
#define PI 3.1415926
#define CIRCLE(R,L,S,V) L=2*PI*R; S=PI*R*R;
V=4/3*PI*R*R*R
main()
{ float r,l,s,v; /*半径、圆周长、圆面积、球体积
*/
scanf("%f",&r);
CIRCLE(r,l,s,v);
printf("r=%6.2f,l=%6.2f,s=%6.2f,v=%6.2f\n",r,l,s,
v);
}
8.3 文件包含( #include)
文件包含命令的一般格式是,
#include,文件名,
作用:预处理时,把, 文件名, 指定的文件内容复制到本文件,再对合并
后的文件进行编译。
例,
在 file1.c文件中,有文件包含命令 #include "file2.c",预处理时,
先把 file2.c的内容复制到文件 file1.c,再对 file1.c进行编译。
从理论上说,#include命令可以包含任何类型的文件,只要这些文
件的内容被扩展后符合 C语言语法。
一般 #include命令用于包含扩展名为,h的, 头文件,,如 stdio.h、
string.h,math.h。在这些文件中,一般定义符号常量、宏,或
声明函数原型。
( 2)文件 file1.c
#include "print_format.h,
main()
{ int a,b,c,d;
char string[] = "CHINA";
a = 1; b = 2; c = 3; d = 4;
PR(D1,a);
PR(D2,a,b);
PR(D3,a,b,c);
PR(D4,a,b,c,d);
PR(S,string);
}
程序员也可以把自己定义的符号常
量、宏,或函数原型放在头文件中,
用 #include命令包含这些头文件。
( 1)文件 print_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"
说明,
1、一个 include命令只能指定一个被包含文件,
如果要包含 n个文件,用 n个 Include命令。
2,#include命令的文件名,可以使用两种括号。
#include "file2.h" 先在引用被包含文件的目
录查找 file2.h文件,若没有,再到系统指定
的目录查找。
#include <file2.h> 仅在系统指定的目录查找
文件 file2.h。
8.4 条件编译
? 预处理程序提供了条件编译的功能 。 可以
按不同的条件去编译不同的程序部分, 因
而产生不同的目标代码文件 。 这对于程序
的移植和调试是很有用的 。
? 条件编译有三种形式, 下面分别介绍,
1,第一种形式,
? #ifdef 标识符
? 程序段 1
? #else
? 程序段 2
? #endif
? 它的功能是, 如果标识符已被 #define命令定
义过则对程序段 1进行编译;否则对程序段 2进
行编译 。 如果没有程序段 2(它为空 ),本格式
中的 #else可以没有, 即可以写为,
? #ifdef 标识符
? 程序段
? #endif
? 2,第二种形式,
? #ifndef 标识符
? 程序段 1
? #else
? 程序段 2
? #endif
?与第一种形式的区别是将, ifdef”改为
,ifndef”。它的功能是,如果标识符未被
#define命令定义过则对程序段 1进行编译,否
则对程序段 2进行编译。这与第一种形式的功能
正相反。
? 3,第三种形式,
? #if 常量表达式
? 程序段 1
? #else
? 程序段 2
? #endif
?它的功能是,如常量表达式的值为真 (非 0),则
对程序段 1 进行编译,否则对程序段 2进行编译。
因此可以使程序在不同条件下,完成不同的功能。
本章小结
预处理是C语言的一个重要功能, 它由预处理程序负责
完成 。 当对一个源文件进行编译时, 系统将自动引用预处理
程序对源程序中的预处理部分作处理, 处理完毕自动进入对
源程序的编译 。
c语言中有多种预处理功能,如宏定义、文件包含、条
件编译等。
合理地使用预处理功能编写的程序便于阅读、修改、移
植和调试,也有利于模块化程序设计。
课堂同步,做做与练练
1、对宏命令的处理是 _ ___
A、在程序执行时进行的
B、在对程序中其他语句进行编译前
进行的
C,在程序连接时进行的与程序中其
他语句同时进行编译
D、与程序中其他语句同时进行编译
课堂同步,做做与练练
2、对宏命令的处理是 _ ___
A、在程序执行时进行的
B、在对程序中其他语句进行编译前
进行的
C,在程序连接时进行的与程序中其
他语句同时进行编译
D、与程序中其他语句同时进行编译
课堂同步,课后练习与作业
1.编写程序题,
输入一行字母字符,根据需要设
置条件编译,使之能使将字母全改
为大写输出,或全改为小写字母输
出。