背景

在编译的时候,出现“redefine”的错误,最后检查才发现对应的头文件没有写正确的预编译信息:

#ifndef _HeadFileName_H
#define _HeadFileName_H // 头文件内容 #endif //_HeadFileName_H

添加后,不再报错,然后就思考,这个“#ifndef #define #endif”的作用到底是什么?于是有了此篇文章。

正文

“#ifndef #define #endif”其实是预编译的一种机制。

在我们的C project中,通常由“.c”“.h”文件构成,“.c”文件即是具体的代码,相应的“.h”文件即是对应“.c”文件的说明。

在预编译的时候,编译器会将“.c”包含的所有的“.h”文件内的函数,宏定义,变量信息给汇聚到一个文件内,但能被写入该文件的条件是,所有的这些函数入口,宏定义,变量信息有且只能有一个,因此,当有多个“.c”文件包含同一个“.h”文件时,就会出现重复定义的错误,所以“#ifndef”、“#define”、“#endif”就派上用场了,当编译器第一次预编译一个“.h”文件时,该文件内的宏则还没有被定义,然后编译器则会先定义该宏,并将该“.h”文件内的信息记录,待第二次编译器又预编译到同一个“.h”文件时,该宏定义已经被定义,则编译器直接跳转到“#endif”,略过此包含。

形象点比喻,整个project就是一个由可以实现各种功能的的厂房组成的工厂,每个“.c”文件对应的即是工厂内的每一个厂房,对应的“.h”则挂在对应的厂房外,标明了这个“.c”厂房内有哪些设备,可以实现哪些功能。而另外一些“.c”厂房如果需要用其他厂房的功能,只需要在他的“.h”标示文件内将其他厂房内的“.h”标示文件写在其标示文件内即可,这就是文件包含(相当于在“.h”文件内包含其他“.h”文件);或者直接在他的“.h”文件旁,挂其他厂房的“.h”标示文件(相当于在.c文件包含其他“.h”文件)。

在实际运行时,整个工厂就相当于一个流水线开始运作了,当运作到“A.c”A厂房的时候,在这个环节内,需要使用“B.c”B厂房内的一个功能,这个流水线就会根据A厂房外挂的B厂房的标示文件“B.h”找到B厂房对应功能的工位地址,接着去运行对应的功能。这就是“.c”、“.h”的功能。

预编译阶段,就相当于将这个工厂组合起来的组织者(编译器)实现工厂整合的过程,可是这个组织者比较笨。他会按照顺序巡视所有的厂房,譬如A厂房,然后一看A厂房的“.h”标示板,就会用个文件记下来,A厂房有功能1,功能2。在巡视A厂房的时候,它看到了B厂房的标识板,组织者(编译器)如实记录A厂房还有B厂房的功能3,功能4,然后检查了下A厂房的设备都没问题(C文件内没有语法错误)。接着组织者(编译器)巡视到在B厂房的标识板时,看到B厂房也有功能3,功能4.组织者(编译器)就不干了,就说这个功能A厂房已经有了,不能重复建设,不环保(- -!),项目不能通过验收,回去重做(报redefine的错误)。

于是工厂建造者(我们程序员)就想起来一个方法“#ifndef #define #endif”,和编译器约定了一个规则,如果碰到标示板(“.h”)文件上没有定义对应的头文件的宏,那就定义它,然后将里面的功能给记录下来,以#endif表示结尾。如下所示,

#ifndef _HeadFileName_H
#define _HeadFileName_H // 头文件内容 #endif //_HeadFileName_H

接着,编译器看到同一个头文件对应的宏定义之前已经定义了,说明这些功能已经记录,编译器就会直接跳转到对应的#endif位置,无视此次包含。由于这些功能此前已经记录,因此不会出现在实际运行时找不到对应的功能入口地址的情况。

以上说的既是多个文件包含同一个头文件,编译器的具体行为。

要强调一点,以上所说的头文件内一定不要定义变量,只能定义宏定义、enmu,函数声明。因为,定义后者,编译器只需要将对应的记号记录到一个文件内即可,定义前者,编译器一定会给它分配实体地址,第二次再来包含的时候,若是在头文件内有变量,有些编译器会无视“#ifndef #define #endif”的约定并再次为它分配地址,然后编译器发现之前已经分配过地址了,还是会报“redefine”的错误。当然只是有些笨的编译器会,已经证实gcc编译器不会有这种笨的处理,但是,为了使自己的代码能兼容更多的的编译器,还是建议不要在“.h”文件内定义变量。

至此,记录完毕。

参考链接

Why use #ifndef CLASS_H and #define CLASS_H in .h file but not in .cpp?;

记录时间:2017-1-6

记录地点:深圳WZ

C/C++头文件使用 #ifndef #define #endif 的原因的更多相关文章

  1. 头文件中ifndef/define/endif的作用以及#pragma once使用

    例如:要编写头文件test.h 在头文件开头写上两行: #ifndef _TEST_H #define _TEST_H//一般是文件名的大写 ············ ············ 头文件 ...

  2. c/c++头文件中#ifndef/#define/#endif的用法

    想必很多人都看过“头文件中用到的 #ifndef/#define/#endif 来防止该头文件被重复引用”.但是是否能理解“被重复引用”是什么意思?头文件被重复引用了,会产生什么后果?是不是所有的头文 ...

  3. 头文件里面的ifndef /define/endif的作用

    c,c++里面,头文件里面的ifndef /define/endif的作用 今天和宿舍同学讨论一个小程序,发现有点地方不大懂······ 是关于头文件里面的一些地方: 例如:要编写头文件test.h ...

  4. C++头文件为什么要加#ifndef #define #endif

    #ifndef 在头文件中的作用 在一个大的软件工程里面,可能会有多个文件同时包含一个头文件,当这些文件编译链接成一个可执行文件时 ,就会出现大量“重定义”的错误.在头文件中实用#ifndef #de ...

  5. 头文件为什么要加#ifndef #define #endif

    #ifndef 在头文件中的作用 在一个大的软件工程里面,可能会有多个文件同时包含一个头文件,当这些文件编译链接成一个可执行文件时 ,就会出现大量“重定义”的错误.在头文件中实用#ifndef #de ...

  6. 头文件中的#ifndef/#define/#endif 的作用

    在一个大的软件工程里面,可能会有多个文件同时包含一个头文件,当这些文件编译链接成一个可执行文件时,就会出现大量重定义的错误.在头文件中实用#ifndef #define #endif能避免头文件的重定 ...

  7. #ifndef #define #endif 防止头文件被重复引用

    想必很多人都看过“头文件中的 #ifndef/#define/#endif 防止该头文件被重复引用”.但是是否能理解“被重复引用”是什么意思?是不能在不同的两个文件中使用include来包含这个头文件 ...

  8. 头文件#ifndef #define #endif使用

    想必很多人都看过“头文件中的 #ifndef #define #endif 防止该头文件被重复引用”.但是是否能理解“被重复引用”是什么意思?是不能在不同的两个文件中使用include来包含这个头文件 ...

  9. 头文件种的ifndef/define/endif 是干什么用的

    头文件种的ifndef/define/endif 是干什么用的? 答:防止头文件被重复包含.

随机推荐

  1. excel基本

    1,换行:control+option(alt)+enter

  2. java jdbc的优化之BeanUtils组件

    1. BeanUtils组件 1.1 简介 程序中对javabean的操作很频繁, 所以apache提供了一套开源的api,方便对javabean的操作!即BeanUtils组件. BeanUtils ...

  3. [LeetCode] Self Crossing 自交

    You are given an array x of n positive numbers. You start at point (0,0) and moves x[0] metres to th ...

  4. JS组件系列——又一款MVVM组件:Vue(一:30分钟搞定前端增删改查)

    前言:关于Vue框架,好几个月之前就听说过,了解一项新技术之后,总是处于观望状态,一直在犹豫要不要系统学习下.正好最近有点空,就去官网了解了下,看上去还不错的一个组件,就抽空研究了下.最近园子里vue ...

  5. 写出将字符串中的数字转换为整型的方法,如:“as31d2v”->312,并写出相应的单元测试,正则去掉非数值、小数点及正负号外的字符串

    写出将字符串中的数字转换为整型的方法,如:"as31d2v"->312,并写出相应的单元测试,输入超过int范围时提示不合法输入. public struct Convert ...

  6. 三款不错的图片压缩上传插件(webuploader+localResizeIMG4+LUploader)

    涉及到网页图片的交互,少不了图片的压缩上传,相关的插件有很多,相信大家都有用过,这里我就推荐三款,至于好处就仁者见仁喽: 1.名气最高的WebUploader,由Baidu FEX 团队开发,以H5为 ...

  7. 庆祝下,提交了第一个ceph pull request。实现了从0到1的突破

    庆祝一下!经过社区老司机的带路,昨天提交了第一个ceph pull request.实现了从0到1的突破,希望再接再厉提交更多代码到社区,为社区发展贡献一点自己力量. 提交的第一个被社区fix的bug ...

  8. Django form

    简单的from验证 文件目录结构 urls.py from app1.views import account urlpatterns = [ url(r'^admin/', admin.site.u ...

  9. day7_subprocess模块和面向对象,反射

    常用subprocess方法示例 #执行命令,返回命令执行状态 , 0 or 非0>>> retcode = subprocess.call(["ls", &qu ...

  10. ABP文档 :Overall - Introduction

    介绍 我们基于不同的需求创建不同的应用,但却在一次又一次地实现相同或相似的结构.至少在某种程度上,授权.验证.异常处理.日志.本地化.数据库连接管理.配置管理.审计日志属于通用的结构. 另外我们总是在 ...