第12章 C语言的编译预处理

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

第12章 C 语言的编译预处理

C 语言属于高级语言,用C 语言编写的程序称为源程序,这种用高级语言编写的源程序

计算机是不能直接执行的,必须经过C 语言的编译系统把源程序编译成目标程序(机器指令构成的程序)并连接成可执行程序,计算机才可以执行。因此,用C 语言来处理问题,必须经过程序的编写→编译及连接→运行三个主要过程。然而,为了减少C 源程序编写的工作量,改善程序的组织和管理,帮助程序员编写易读、易改、易于移植、便于调试的程序,C 语言编译系统提供了预编译功能。

所谓的预编译功能是指:编译器在对源程序正式编译前,可以根据预处理指令先做一些

特殊的处理工作,然后将预处理结果与源程序一起进行编译。

C 语言提供的编译预处理功能主要有三种:文件包含、宏定义、条件编译。这三种功能

分别以三条编译预处理命令#include 、#define 、#if 来实现。编译预处理指令不属于C 语言的

语法范畴,因此,为了和C 语句区别开来,预处理指令一律以符号“#”开头,以“回车” 结束,每条预处理指令必须独占一行。

12.1 文件包含预处理

“包含”的英文单词为“include ”,所谓“文件包含”预处理,就是在源文件中通过“#include ”命令指示编译器将另一段源文件包含到本文件中来。

例如,源文件f1.c 中有一句“#include f2.c ”编译预处理命令,如图12-1(a)所示。编译预处理后文件f1.c 的完整结构如图12-1(c)所示。

图12-1 文件包含编译预处理命令

编译时先将f2.c (图12-1(b))的内容复制嵌入到f1.c (图12-1(a))中来,即进行“包含”

预处理,然后对调整好的完整的f1.c (图12-1(c))进行编译,得到相应的目标代码。换句话说,由f1.c 和f2.c 组成程序的目标代码(.obj )和用一个源文件(类似于图c )的目标代码(.obj )完全一样。但是用#include 包含f2.c 的方式编写程序可以使其他的程序重用f2.c 的代码,并且使源文件简洁明了。

“文件包含”指令有两种使用方式:

第一种形式,用尖括号(即小于号<、大于号>)括起被包含源文件的名称:

#include <文件名> f1.c ……… ……… ……… ……………… ……………… ……………… ……………… ……………… ……………… #include f2.c ……………… ……………… ……………… ……………… ……………… ………………

f1.c (a) 预编译前 (b) (c) 预编译后

第二种形式,用双引号括起被包含源文件的名称:

#include 文件名

文件名按操作系统的要求定义,可以包括路径信息,例如:

#include

#include xyz.c

#include c: \bc\mydir\head2.h

#include <文件名>方式常用来“包含”系统头文件。系统头文件一般存储在系统指定的目录中,如Turbo C的include子目录。当C编译器识别出这条#include <文件名>命令后,它不搜索当前子目录,而直接到系统指定的包含子目录(即include子目录)中去搜索相应的头文件,并将搜索到的头文件的内容“包含”到“主”文件中来。

#include 文件名方式常用来“包含”程序员自己建立的头文件。当编译器识别出这条#include 文件名命令后,它先搜索“主”文件所在的当前子目录,如果没找到再去搜索相应的系统子目录。

注意,所谓文件包含,指的是在“主”文件源程序中嵌入另一些源程序语句,形成一个完整的源程序去进行编译。所以只能包含源文件而不能去包含目标文件。

如果源文件1中包含源文件2,而源文件2中又包含源文件3,这就是所谓的嵌套包含。

例12.1将若干个系统头文件包含到本文件中来。

#include

#include

#include

void main() {

}

其中stdio.h头文件含有与标准输入输出操作有关的函数的原型声明等,如getc、putchar 函数;math.h中则是一些数学函数的原型声明;而atoi函数、exit函数等的原型声明在stdlib.h 头文件中。这些都是系统头文件,存储在系统指定的include子目录中,所以程序中使用包含命令时用<>括起头文件名,以便C编译器直接搜索系统子目录,快速寻找到这些头文件。一条#include命令只能包含一个文件,若要包含多个文件就必须使用多条#include命令。

一个C程序通常由多个源文件组成,每个源文件都是一个可独立编译的程序单位。在将程序分解成多个源文件后,必须计划每个源文件中哪些信息其他文件可见(以源文件形式提供),哪些不可见(以目标代码形式提供)。我们通常的做法是把其他文件可见的信息放在一个称为“头文件(.h)”的源文件中,在需要的文件中用#include预编译指令包括进去。

“头文件”中可以包含哪些代码,不能包含哪些代码?C语言的语法没有强行的规定。根据经验的总结,以下内容放在头文件中比较合适:

(1) 包含指令(嵌套),如:

#include

(2) 函数声明,如:

extern int fn(int a);

(3) 类型声明,如:

enum BOOLEAN {false, true};

(4) 常量定义,如:

const float pi=3.14159;

(5) 数据声明,如:

extern int m; extern int a[];

(6) 宏定义,如:

#define PI 3.1459

而对于函数的定义,数据的定义等代码不宜包含在头文件中。

12.2 宏定义预处理

熟悉汇编语言的读者都知道,宏是宏汇编语言的一个组成部分。C语言借鉴这种特点,同样具有宏的组成。C语言宏定义的简单形式是不带参数的宏定义,即符号常量定义,而带参数的宏定义则是它的复杂形式。

12.2.1不带参数的宏定义

不带参数的宏定义常用来定义符号常量。程序总是用来处理具体问题的,因此程序中用到的常量一般都有具体的物理含义。但从常数本身却看不出它的物理含义,这显然降低了程序的可读性。因此C语言提供符号常量定义的预处理手段,指定一个有物理含义的名称(标识符)来代表一个具体常量(可以把它看做是一个可替换的正文字串)。不带参数的宏定义的一般形式为:

#define 标识符具体常量

#define 宏名替换字串

这种方法使得用户能以一个简单易记的常量名称代替一个较长而难记的具体常量,人们把这个标识符(名称)称为“宏名”,预处理时用具体的常量字符串替换宏名,这个过程称为“宏展开”。例:

#define PI 3.14159

它的作用是指定名称PI对应常数“3.14159”,程序中原先需要使用3.14159而其含义又为圆周率的地方都可以改用PI。这样一来,凡是程序中出现PI的地方,经预处理后都会被替换成3.14159。

例12.2计算圆周长、圆面积以及同半径的球表面积和球体积。

#include

#define PI 3.14159

void main() {

float r, c, a, s, v;

printf(Radius=);

scanf(%f, &r);

c=2.0*PI*r;

a=PI*r*r;

s=4.0*PI*r*r;

v=4.0/3*PI*r*r*r;

相关文档
最新文档