您当前的位置:首页 > 计算机 > 编程开发 > C语言

防止C语言头文件被重复包含

时间:12-30来源:作者:点击数:

头文件包含命令 #include 的效果与直接复制粘贴头文件内容的效果是一样的,预处理器实际上也是这样做的,它会读取头文件的内容,然后输出到 #include 命令所在的位置。

头文件包含是一个递归(循环)的过程,如果被包含的头文件中还包含了其他的头文件,预处理器会继续将它们也包含进来;这个过程会一直持续下去,直到不再包含任何头文件,这与递归的过程颇为相似。

递归包含会导致一个问题,就是重复引入同一个源文件。例如在某个自定义头文件 xyz.h 中声明了一个 FILE 类型的指针,以使得所有的模块都能使用它,如下所示:

extern FILE *fp;

FILE 是在 stdio.h 中自定义的一个类型(本质上是一个结构体),要想使用它,必须包含 stdio.h,因此 xyz.h 中完整的代码应该是这样的:

#include <stdio.h>
extern FILE *fp;

现在假设程序的主模块 main.c 中需要使用 fp 变量和 printf() 函数,那么就需要同时引入 xyz.h 和 stdio.h:

#include <stdio.h>
#include "xyz.h"
int main(){
    if( (fp = fopen("demo.txt", "r")) == NULL ){
        printf("File open failed!\n");
    }
    //TODO:
    return 0;
}

这样一来,对于 main.c 这个模块,stdio.h 就被包含了两次。stdio.h 中除了有函数声明,还有宏定义、类型定义、结构体定义等,它们都会出现两次,如果不做任何处理,不仅会出现重复定义错误,而且不符合编程规范。

有人说,既然已经知道 xyz.h 中包含了 stdio.h,那么在 main.c 中不再包含 stdio.h 不就可以了吗?是的,确实如此,这样做就不会出现任何问题!

现在我们不妨换一种场景,假设 xyz1.h 中定义了类型 RYPE1,xyz2.h 中定义了类型 TYPE2,并且它们都包含了 stdio.h,如果主模块需要同时使用 TYPE1 和 TYPE2,就必须将 xyz1.h 和 xyz2.h 都包含进来,这样也会导致 stdio.h 被重复包含,并且无法回避,上面的方案解决不了问题。

实际上,头文件的交叉包含是非常普遍的现象,不仅我们自己创建的头文件是这样,标准头文件也是如此。例如,标准头文件 limits.h 中定义了一些与数据类型相关的宏(最大值、最小值、一个字节所包含的比特位等),stdlib.h 就包含了它。

我们必须找到一种行之有效的方案,使得头文件可以被包含多次,但效果与只包含一次相同。

在实际开发中,我们往往使用宏保护来解决这个问题。例如,在 xyz.h 中可以添加如下的宏定义:

#ifndef _XYZ_H
#define _XYZ_H
/* 头文件内容 */
#endif

第一次包含头文件,会定义宏 _XYZ_H,并执行“头文件内容”部分的代码;第二次包含时因为已经定义了宏 _XYZ_H,不会重复执行“头文件内容”部分的代码。也就是说,头文件只在第一次包含时起作用,再次包含无效。

标准头文件也是这样做的,例如在 Visual Studio 2010 中,stdio.h 就有如下的宏定义:

#ifndef _INC_STDIO
#define _INC_STDIO
/* 头文件内容 */
#endif

这种宏保护方案使得程序员可以“任性”地引入当前模块需要的所有头文件,不用操心这些头文件中是否包含了其他的头文件。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门