c语言入门28,头文件的使用
发表于: 2018-11-27 20:36:15 | 已被阅读: 69 | 分类于: C语言
上一节介绍了使用 C 语言开发大项目时,多文件编程的开发方法。fun.c 这个模块里定义了 add 函数,以后想使用 add 函数,只需要从 fun.c 文件 extern add 函数即可。但是使用 fun.c 模块的每个文件都需要重新声明 add 函数也是很麻烦的。前面的文章,我们一直强调

以前,我们都是把重复代码尽力封装为函数,增加代码复用性。那这次为了不重复声明模块里定义的函数,有什么办法呢?答案就是使用
// fun.h
#ifndef _FUN_H_
#define _FUN_H_
extern int add(int a, int b);
extern int cnt;
#endif // _FUN_H_

可以看出,如果想在 main.c 文件中使用 fun.c 文件中定义的函数,只需要包含 fun.h 就可以了。事实上,以后任何模块想调用 fun.c 中的函数,都只需包含 fun.h 即可。关于 fun.h 中内容,有几点细节。
先再说说为什么 #include <stdio.h>用角括号,而#include "fun.h"用引号。对于用尖括号包含的头文件,编译器会从系统的头文件目录查找。例如我的 codeblocks 的系统头文件路径:


#ifndef _FUN_H_
#define _FUN_H_
//...
#endif
这几句属于条件编译语句,意思是如果没有 define

那为什么需要防止重复包含呢?谁会把一个头文件包含两次呢?像上面那么明显的错误没人会犯,但有时候重复包含的错误并不是那么明显的。在规模较大的项目中头文件包含头文件的情况很常见,经常会包含四五层,这时候重复包含的问题就很难发现了。比如在我的系统头文件录/usr/include中,errno.h包含了bits/errno.h,后者又包含了linux/errno.h,后者又包含了asm/errno.h,后者又包含了asm-generic/errno.h。
另外一个问题是,就算我是重复包含了头文件,那有什么危害么?像上面的三个函数声明,在程序中声明两次也没有问题,对于具有External Linkage的函数,声明任意多次也都代表同一个函数。重复包含头文件有以下问题:
- 一是使预处理的速度变慢了,要处理很多本来不需要处理的头文件。
- 二是如果有互相包含的情况,预处理器就陷入死循环了(其实编译器都会规定一个包含层数的上限)。
- 三是头文件里有些代码不允许重复出现,虽然变量和函数允许多次声明(只要不是多次定义就行),但头文件里有些代码是不允许多次出现的,比如typedef类型定义和结构体Tag定义等,在一个程序文件中只允许出现一次。

还有一个问题,