C/C++头文件使用 #ifndef #define #endif 的原因
背景
在编译的时候,出现“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 的原因的更多相关文章
- 头文件中ifndef/define/endif的作用以及#pragma once使用
例如:要编写头文件test.h 在头文件开头写上两行: #ifndef _TEST_H #define _TEST_H//一般是文件名的大写 ············ ············ 头文件 ...
- c/c++头文件中#ifndef/#define/#endif的用法
想必很多人都看过“头文件中用到的 #ifndef/#define/#endif 来防止该头文件被重复引用”.但是是否能理解“被重复引用”是什么意思?头文件被重复引用了,会产生什么后果?是不是所有的头文 ...
- 头文件里面的ifndef /define/endif的作用
c,c++里面,头文件里面的ifndef /define/endif的作用 今天和宿舍同学讨论一个小程序,发现有点地方不大懂······ 是关于头文件里面的一些地方: 例如:要编写头文件test.h ...
- C++头文件为什么要加#ifndef #define #endif
#ifndef 在头文件中的作用 在一个大的软件工程里面,可能会有多个文件同时包含一个头文件,当这些文件编译链接成一个可执行文件时 ,就会出现大量“重定义”的错误.在头文件中实用#ifndef #de ...
- 头文件为什么要加#ifndef #define #endif
#ifndef 在头文件中的作用 在一个大的软件工程里面,可能会有多个文件同时包含一个头文件,当这些文件编译链接成一个可执行文件时 ,就会出现大量“重定义”的错误.在头文件中实用#ifndef #de ...
- 头文件中的#ifndef/#define/#endif 的作用
在一个大的软件工程里面,可能会有多个文件同时包含一个头文件,当这些文件编译链接成一个可执行文件时,就会出现大量重定义的错误.在头文件中实用#ifndef #define #endif能避免头文件的重定 ...
- #ifndef #define #endif 防止头文件被重复引用
想必很多人都看过“头文件中的 #ifndef/#define/#endif 防止该头文件被重复引用”.但是是否能理解“被重复引用”是什么意思?是不能在不同的两个文件中使用include来包含这个头文件 ...
- 头文件#ifndef #define #endif使用
想必很多人都看过“头文件中的 #ifndef #define #endif 防止该头文件被重复引用”.但是是否能理解“被重复引用”是什么意思?是不能在不同的两个文件中使用include来包含这个头文件 ...
- 头文件种的ifndef/define/endif 是干什么用的
头文件种的ifndef/define/endif 是干什么用的? 答:防止头文件被重复包含.
随机推荐
- js入门学习~ 运动应用小例
要实现的效果如下: 鼠标移入各个小方块,实现对应的效果(变宽,变高,移入透明,移出恢复)~~ (且各运动相互之前不干扰) 主要是练习多个物体的运动框架~~ --------------------- ...
- Resources.Load加载文件返回null的原因
1.文件夹都要放在Resources目录下 2.加载时photoName不需要扩展名 Texture2D t = Resources.Load<Texture2D>("Loadi ...
- iOS CALayer应用详解
跟着大神一起进步,本篇博客原文地址:http://blog.csdn.net/hello_hwc?viewmode=contents 一 CALayer是什么? Layers是绘图和动画的基础, L ...
- JSP九大内置对象及四个作用域
九大对象: 内置对象(又叫隐含对象,有9个内置对象):不需要预先声明就可以在脚本代码和表达式中随意使用 1-out: javax.servlet.jsp.JspWriter类型,代表输出流的对象.作用 ...
- EF里的默认映射以及如何使用Data Annotations和Fluent API配置数据库的映射
I.EF里的默认映射 上篇文章演示的通过定义实体类就可以自动生成数据库,并且EF自动设置了数据库的主键.外键以及表名和字段的类型等,这就是EF里的默认映射.具体分为: 数据库映射:Code First ...
- [LeetCode] Nth Digit 第N位
Find the nth digit of the infinite integer sequence 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ... Note: n i ...
- [LeetCode] Find Peak Element 求数组的局部峰值
A peak element is an element that is greater than its neighbors. Given an input array where num[i] ≠ ...
- JAVA的反射理解
1----------------------------反射的概念----------------------------------------------- JAVA的反射机制是在运行状态中,对 ...
- 三款不错的图片压缩上传插件(webuploader+localResizeIMG4+LUploader)
涉及到网页图片的交互,少不了图片的压缩上传,相关的插件有很多,相信大家都有用过,这里我就推荐三款,至于好处就仁者见仁喽: 1.名气最高的WebUploader,由Baidu FEX 团队开发,以H5为 ...
- LVS原理详解
一.集群简介 什么是集群 计算机集群简称集群是一种计算机系统,它通过一组松散集成的计算机软件和/或硬件连接起来高度紧密地协作完成计算工作.在某种意义上,他们可以被看作是一台计算机.集群系统中的单个计算 ...