16.5 文件包含:#include

预处理器发现#include指令后,就会寻找后跟的文件名并把这个文件的内容包含到当前文件中。被包含文件中的文本将替换源代码文件中的#include指令,就像您把被包含文件中的全部内容键入到源文件中的这个特定位置一样。#include指令有两种使用形式,请参见表16.1。

表16.1  #include指令的两种使用形式

 

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

 

#include <stdio.h> 文件名放在尖括号中
#include “mystuff.H” 文件名放在双引号中

在UNIX系统中,尖括号告诉预处理器在一个或多个标准系统目录中寻找文件。双引号告诉预处理器先在当前目录(或文件名中指定的其他目录)中寻找文件,然后在标准位置寻找文件。表16.2中是一些例子。

表16.2 使用#include指令的一些例子

 

 

#include <stdio.h> 搜索系统目录
#include “hot.H” 搜索当前工作目录
#include “/usr/biff/p.h” 搜索/usr/biff目录

对于系统头文件,集成开发环境(IDE)具有标准搜索路径。许多集成开发环境提供菜单选项用于指定使用尖括号时搜索的其他路径。对于UNIX,使用双引号意味着首先搜索本地目录,但是具体搜索哪个目录依赖于编译器。有些编译器搜索源代码文件所在目录,有些则搜索当前工作目录,还有些搜索工程文件所在目录。

因为计算机系统的结构不完全相同,所以ANSIC不要求对文件采用一样的目录模式。一般而言,命名文件的方法依赖于系统,但是尖括号和双引号的使用则与系统无关。

为什么要包含文件呢?因为这些文件包含了编译器所需的信息。例如,stdio.h文件通常包含EOF、NULL、getchar ( )和putchar ( )的定义。getchar ( )和putchar ( )作为宏函数定义。stdio.h文件还包含C的I/O函数的原型。

习惯上使用后缀.h表示头文件(header file),这类文件包含置于程序头部的信息。头文件经常包含预处理器语句。有些头文件(如stdio.h)由系统提供。但您也可以自由创建您自己的头文件。

包含大型头文件并不一定显著增加程序的大小。很多情况下,头文件中的内容是编译器产生最终代码所需的信息,而不是加到最终代码里的具体语句。

16.5.1 头文件:一个实例

假设您要开发一个存放人名的结构,并编写一些使用该结构的函数。您可以在一个头文件中集中进行各种声明。程序清单16.6给出了这个例子:

程序清单16.6 names.h头文件

阅读 ‧ 电子书库

这个文件中含有在头文件中常见的许多内容:#define指令、结构声明、typedef语句和函数原型。注意这些内容都不是可执行代码,而是编译器用于产生可执行代码的信息。

可执行代码通常在源代码文件中,而不在头文件中。例如,程序清单16.7显示了头文件中函数原型的函数定义。源文件包含了头文件,因此编译器将知道关于names类型的信息。

程序清单16.7 names_st.c源文件

阅读 ‧ 电子书库

get_names ( )函数使用fgets ( )以避免目标数组溢出。如果被保存的字符串中存在换行符,则用空字符代替该换行符。如果没有换行符,表明fgets ( )在到达行尾之前就被停止,代码会去掉该行中剩余的输入。

程序清单16.8是使用前面的头文件和源代码文件的示例程序。

程序清单16.8 useheader.c程序

阅读 ‧ 电子书库

阅读 ‧ 电子书库

下面是一个运行示例:

阅读 ‧ 电子书库

关于该程序,应注意以下几点:

● 两个源代码文件都使用了names_st结构,因此,它们都必须包含头文件names_st.h。
● 需要编译、链接names_st.c和useheader.c两个源代码文件。
● 声明及类似语句放在头文件names_st.h中,函数定义放在源代码文件names_st.c中。
16.5.2 使用头文件

浏览任何一个标准头文件都会使您对头文件中信息的类型有一个清晰的概念。头文件内容的最常见的形式包括:

● 明显常量——例如,典型的stdio.h文件定义EOF、NULL和BUFSIZE (标准I/O缓冲区的大小)。
● 宏函数——例如,getchar ( )通常被定义为getc (stdin),getc ( )通常被定义为较复杂的宏,而头文件ctype.h通常包含ctype函数的宏定义。
● 函数声明——例如,头文件string.h(在某些旧的系统中为strings.h)包含字符串函数系列的函数声明。在ANSIC中,声明采用函数原型形式。
● 结构模板定义——标准I/O函数使用FILE结构,该结构包含文件及文件相关缓冲区的信息。头文件stdio.h中存放FILE结构的声明。
● 类型定义——可以使用指向FILE的指针作为参数调用标准I/O函数。通常,stdio.h用#define或typedef使得FILE代表指向FILE结构的指针。与之类似,size_t和time_t类型也在头文件中定义。

许多程序员开发自己的标准头文件以便在程序中使用。如果是在开发一系列相关的函数和(或)结构,那么这种方法特别有价值。

另外,可以使用头文件来声明多个文件共享的外部变量。例如,如果开发共享某个变量的一系列函数,该变量报告某种类型(如错误情况)的状态,这种方法就很有用处。此时,可以在包含函数声明的源代码文件中定义一个具有文件作用域、外部链接的变量。

阅读 ‧ 电子书库

接着,可以在与源代码文件相关联的头文件中进行引用声明:

阅读 ‧ 电子书库

该行代码会出现在包含了该头文件的任何文件中,并使得使用这个函数系列的文件可以使用该变量。在源代码文件中包含该头文件后,该声明也会出现;但是只要声明类型一致,那么在同一文件中使用定义声明和引用声明就不会出现问题。

需要包含头文件的另一种情况是:使用具有文件作用域、内部链接和const限定词的变量或数组。使用const可以避免值被意外地改变。使用static后,每个包含该头文件的文件都获得一份该常量的副本。因此,不存在这样的问题,即需要在一个文件中进行定义声明,并且需要在其他文件中进行引用声明。

#include和#define指令是最常用的C预处理器的两个功能。下面将简要介绍其他指令。