2个办法解决头文件重复包含的问题

前言

在一些大型项目中,各种头文件会相互包含,关系就显得错综复杂,同时由于文件的包含就是复制粘贴,那些重复包含的头文件就会使代码长度大大增加,那有没有什么办法解决这个问题呢?答案肯定是有的,这就涉及到条件编译了。

条件编译

所谓条件编译就是在编译阶段让编译器选择编译或不编译某些代码,需要注意的是条件编译是在编译阶段进行的,而不是等到程序运行时,利用这点我们就可以让编译器选择是否包含头文件,从而避免头文件被重复包含。
下面看看一些常见的条件编译指令:
1.

#if  ...(常量表达式)
...(内容)
#endif

例:

#define MAX 100

#if MAX//如果MAX为真,则定义常量MIN
#define MIN 1
#endif

2.多个分支的条件编译

#if ...(常量表达式1)
...(内容1)
#elif ...(常量表达式2)
...(内容2)
#else
...(内容3)
#endif

例:

#define MAX 100
#define MIN 0

#if MIN
	#define RET 0//MAX为真,编译这句代码
#elif MAX
	#define RET 1
#else
	#define RET 2
#endif

3.判断是否被定义

#if defined ...(某个标识符)
...(内容)
#endif

也可以写成以下形式

#ifdef ...(某个标识符)
...(内容)
#endif

---------------------------------------------------------------------

#if !defined ...(某个标识符)
...(内容)
#endif

也可以写成以下形式

#ifndef  ...(某个标识符)
...(内容)
#endif

#define MIN 0

#ifdef MIN
	#define RET 1//MIN已经在前面定义了,故编译这句代码
#endif

4.嵌套指令

类似于以下这种

#ifndef TAP1
  #ifdef SIGN1
	  ...
  #endif
  #ifdef SIGN2
  	  ...
  #endif
#elseif defined TAP2
  ...
#endif

其中第3条编译指令–判断是否被定义是我们这篇文章的重点,希望读者先理解第3条编译指令的用法再往下读。

头文件的嵌套包含与2种解决办法

假设现在有两个文件test1.h和test2.h,由两个程序员编写,最后再合并,我们避免不了会写出以下这样的头文件

test1.h

#include<stdio.h>

test2.h

#include<stdio.h>
#include"test1.h"

在test2.h中就重复包含了stdio.h这个头文件(test2.h自己包含了一次stdio.h,在包含test1.h时又间接包含了stdio.h),那如何解决这个问题呢?下面提供两种解决办法:
1.

在每个头文件中写这样的代码:

#ifndef FILENAME(任意某个未定义过的标识符,最好用该头文件的名字)
#define FILENAME
...//头文件内容
#endif

如在test1.h这样写

#ifndef _TEST1_H_
#define _TEST1_H_
#include<stdio.h>
#endif

2.
也可以在每个头文件中加上下面这句代码:

#pragma once

有些编译器会在头文件开头自动加上这句代码,但一些比较古老的编译器是不支持这种写法的。

以上就是解决头文件重复包含的两种办法,需要注意的是,头文件里面不要定义全局变量或函数(可以声明),否则就会出错,例如某个程序有一个头文件test1.h和2个源文件test2.c和test3.c,其内容如下:
test1.h

#ifndef _TEST1_H_
#define _TEST1_H_

#include<stdio.h>
int a=0;//该头文件定义了一个全局变量

#endif

test2.c

#include"test1.h"
#include"test1.h"

test3.c

#include"test1.h"
#include"test1.h"

int main()
{
	printf("%d",a);
	return 0;
}

编译是以C文件为单位进行编译的,且头文件的包含就是将头文件的内容复制粘贴到当前文件中,所以在单独编译test2.c时没有什么问题(尽管包含了两次头文件,看似a被定义了两次,但由于#ifndef在起作用,所以在test2.c中头文件只被复制粘贴了一次,即a只被定义了一次),接着在单独编译test3.c时也没有什么问题,但在链接阶段就出问题了,链接器在将这两个C文件进行链接时,发现里面有2个a,且都是有效的,于是就报错了。所以在头文件中千万不要定义全局变量和函数,否则你就是公司的神仙队友。(关于编译链接如果有不懂的可以去看看我的《程序的翻译环境和运行环境》 亦星编程

头文件包含的两种方式及区别

相信有细心的读者已经发现了在前面的例子中,我有时会用尖括号包含头文件,有时又会用双引号包含头文件,这里再讲讲头文件的两种包含方式及区别

1. #include"filename"

像这种用双引号括起来的头文件,编译时首先在源文件所在目录下面查找头文件,如果没有找到,编译器就会像查找库函数头文件那样在标准位置查找头文件,如果也没有找到,就会提示编译错误。

2. #include<filename>
这种用尖括号括起来的头文件,编译器就会直接去标准路径下去查找,如果找不到,就提示编译错误。

在知道这两种头文件包含方式的区别后,或许有的人就会想:那我以后可不可以都用双引号来包含头文件呢?这当然是可以的,但这样头文件的查找效率就会低一些(库文件明明是去标准路径下去查找,但由于是双引号包含的头文件,还得到源文件当前目录下去白跑一趟),同时也不容易区分该文件是库文件还是用户自己定义的头文件,所以建议库文件用尖括号,用户自己定义的文件用双引号。

结语

相信通过本片文章,你对头文件的知识有了一个更深的理解,如果本文有什么讲得不对的地方,恳请指正,如果你对本文有什么好的建议,也十分欢迎您的反馈,您的每一次反馈将会铸就下一次更好的体验,当然了,如果你觉得本文对你帮助很大,动动小手,给博主一个点赞收藏加关注吧。

C语言中的头文件互相包含问题,是指多个头文件之间相互引用、相互包含而导致的编译错误。当两个头文件相互引用时,会造成无限循环的包含,使得编译器无法正常解析和处理。 这个问题通常由于程序的模块化设计不合理所导致。在C语言中,头文件的作用是引入函数、变量的声明和定义,以供其他源文件使用。如果两个或多个头文件之间相互引用,意味着它们之间存在依赖关系,即一个头文件中引用了另一个头文件中的函数或变量。 当头文件A引用了头文件B,而头文件B又引用了头文件A时,编译器会陷入无限循环,因为当编译器处理头文件A时,会读取并解析头文件B,而在处理头文件B时,又会读取并解析头文件A,如此循环下去,直到编译器出现错误。 为了解决头文件互相包含问题,可以采取以下方法: 1. 使用预编译指令#ifndef、#define和#endif来避免重复包含。在头文件的开头和结尾使用这些指令,可以防止头文件重复引用。 2. 使用前向声明来替代包含头文件。如果只需要引用函数或变量的声明而不需要其具体定义,可以使用前向声明来替代包含头文件的方式,以减少头文件之间的相互依赖。 3. 重新设计程序结构,减少头文件之间的相互引用。通过合理的程序设计,可以降低模块之间的耦合性,减少或避免头文件互相包含问题。 综上所述,头文件互相包含问题是C语言中常见的编译错误,通过采取预编译指令、前向声明和重新设计程序结构等方法,可以有效地解决这个问题
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值