极客秀
搜索

C/C++ :快来看看使用头文件过程中可能会遇见的大坑和千字避坑指南!

前段时间发过一篇关于C语言使用头文件进行模块化编程的教程。

[ C语言:编写头文件,使用模块化编程

](https://pic.qr2c.cn/s?__biz=MzkyMTUyMDE1OA==&mid=2247485644&idx=1&sn=715948732ba6a30ac437e7581241a05b&chksm=c18319bdf6f490abc9b7ce2bedf4a1291054111e294d5081f321677f117a8bd99bada2de7910&scene=21#wechat_redirect)

正好晚上有一个朋友问我在修改头文件顺序之后出现了一系列报错。

主要的问题呢重定义:multiply defined重定义。 下面呢我就阐述几个头文件编写中常常会出现的几个坑! 编译前的判断


#ifndef __2_H__#define __2_H__  
int a;  
#endif //

这是一个常见的头文件,名字为2.h,首先是头文件最开始的内容#ifndef 配合后面的#endif,他构成了最基本的一条判断。
判断该头文件是否被定义过了。

假如现在我们有两个文件,同时导入了这个头文件。 这时候就会出现变量的重定义。

因为我们在编译这两个文件的时候,会连接.h文件,这样子会多次编译.h文件。
因此出现 同一个变量被多次编译 的问题! 本质上这种问题都是变量多次编译的问题。
而使用#ifndef 和#endif的用处就是防止同一头文件调用在多个.c文件中被调用的时候导致变量的重复定义。


#ifndef __2_H__#define __2_H__  
int a;  
#endif //

这时候,我们再次编译我们的内容,就不会有问题。
同名变量的重复定义


#include "2.h"#include "1.h"  
int main(){  
  
}

假如我们有两个头文件,这两个头文件都包含了有一句int a。我们都在其中定义了一个变量,不过这个变量是同名变量。这样子就会导致这个同名变量重复定义。


#ifndef __1_H__#define __1_H__int a;  
#endif // !1  

同样的,如果有同名函数(C++有重载,但是重载的条件必须是传入参数不同)也会出现同样的情况。
这也是为什么我们不推荐在头文件中定义全局变量以及在头文件中进行函数定义(函数定义和声明是有区别的),而是建议给每一个头文件都配备一个.c文件,将需要的全局变量定义

将一些全局变量在对应的.c文件中进行定义,这样子就不会出现头文件中被重复定义的事情。

这样子我们就不会出现变量被重复定义的问题了。
但是问题来了,我们怎么在其他文件中改变这个变量和使用这个变量呢? 这时候我们就可以通过几个函数构建桥梁。


#include "2.h"  
static  int a;  
int ReturnA(){  return a;}  
void ChangeA(int A){  a = A;}

这里有一种C++中类的思想,每一个.h文件和.c文件都有其“成员”和“方法”,而这里的变量a更像是一个私有变量,除了.c文件之外,其他的文件没办法访问这个变量a,但是我们可以创建两个“方法”,来将a的值进行改变和传递。
需要注意的是,这里需要为变量加上static,这样子才可以规定这个变量在本文件有效!
这里还需要在对应的.h文件中定义这两个函数,让其他的文件可以调用这两个函数。

这样子就可以避免因为头文件中因为同名头文件的冲突啦。
** 此外,还有利用extern 修饰变量。 **


#pragma once#ifndef __2_H__#define __2_H__  
extern int a;  
  
#endif //

在头文件中我们定义一个int a,但是这个int a我们需要使用extern来进行修饰。关于extern的介绍可以看很早期的一段内容:

[ C语言 —— extern外部变量以及函数声明和定义

](https://pic.qr2c.cn/s?__biz=MzkyMTUyMDE1OA==&mid=2247484255&idx=1&sn=7af19e0596c6161abb7228cb27e5ce8e&chksm=c183122ef6f49b38a6e5e19904aa1bf625d75f1a02b8dabbe9455da1163140ad0ff5cc4cc956&scene=21#wechat_redirect)

当编译器遇到extern修饰的变量时,会去其各个附属文件(但凡引用了这个头文件)寻找关于其的定义。
所以我们可以在头文件中引用extern,之后在某个具体的文件中定义这个变量。


#ifndef __2_H__#define __2_H__  
extern int a;  
  
#endif //

这时候我们编译这个工程是并不会报错的。


#include "2.h"void Change1A(){  a = 15;}

并且其各个文件也可以调用这个a,这个a在其头文件和各文件中是通用的。

可以看到,输出的结果分别是0,30,15.可以表示这里的a是通用的被连接到了一起。
但是使用这个方法也需要注意,可以有很多个extern修饰的变量,但是实际非extern修饰的变量的只能有一个。
但是但是,这里其实也可以调用在某个.c文件中定义一个static修饰的变量。实际上发现static修饰的变量和头文件的变量会冲突,这个的冲突不是编译冲突!
按理来说,我调用的a是extern的a,然后Change1A这个函数改变的a,经过测试之后发现是static修饰的a,也许是就近原则(可能),这样子导致我调用Change1A的时候,main中定义的a的并没有发生变化。
这可以证明在.c文件中,先使用的是static修饰的变量。
** 总结 **
其实无论是什么方法,我们都应该避免头文件被重复定义,同名变量不应该出现在代码中,优秀的代码编辑习惯可以极大的提高我们的工作效率和容错率。这无论是给其他人阅读代码还是我们自己阅读我们的代码都带来了便利。
无论是extern和static,最好的方法还是遵守编程规范,少数的巧妙运用但是不能靠这种方式来填坑Bug。

1.转载请保留原文链接谢谢!
2.本站所有资源文章出自互联网收集整理,本站不参与制作,如果侵犯了您的合法权益,请联系本站我们会及时删除。
3.本站发布资源来源于互联网,可能存在水印或者引流等信息,请用户擦亮眼睛自行鉴别,做一个有主见和判断力的用户。
4.本站资源仅供研究、学习交流之用,若使用商业用途,请购买正版授权,否则产生的一切后果将由下载用户自行承担。
5.联系方式(#替换成@):pm#vimge.com

  相关内容