1.深入理解C/C++中头文件(.h)与源文件(.c/.cpp)以及我们为什么需要.h头文件
1.深入理解C/C++中头文件(.h)与源文件(.c/.cpp)以及我们为什么需要.h头文件
本篇文章灵感来自于:https://www.cnblogs.com/laojie4321/archive/2012/03/30/2425015.html
本文章中所有表情包均来自互联网,如有侵权,劳请联系。
另外,转载请标明出处哦,语雀:https://www.yuque.com/yifeideshijie
上面的文章很清楚地解析了在整个项目编译过程中的过程,头文件(通常的.h文件)和源文件(.cpp)文件是如何发挥作用的,但总体逻辑上有些混乱,我以问题的形式对上述文章中的答案进行总结:
上述文章主要回答了如下几个问题,这些问题如果学习过想Java这样语法规范规整的语言之后再学习C/C++就会很容易出现。
1.C/C++中的.h文件(头文件)和.c/.cpp(源文件)到底有什么联系和区别
首先明确.c和.h本质上没有任何区别。
只不过一般(为什么一般要这么做呢,别急,往下看到第3点就可以知道答案了):
.h文件是头文件,内含函数,变量声明,宏声明,结构体声明等内容,.c文件是程序文件,内含函数实现,变量定义等内容。
如果你非要将.h改成.ctest(随便写的)文件可以吗?
当然可以了,你还是想往常一样#include "filename.ctest"就行了。所以实际上你只需要保证有个main入口,在visual studio中你甚至不需要一定保证存在.cpp文件,后缀名都是人为规定的,程序照样可以运行。
所以,本质上讲,.h和.cpp文件没多大区别,不妨就把他们理解为文件。
(这里注意,在linux中用gcc时,会根据你的后缀名来调用相关的编译程序,这里为了以防万一,还是得按照规范来,至于这方面的深入探讨,这里不再做过多解释)
但对于Java而言,这几乎是不能忍受的,因为java代码必须要以.java为结尾才能编译。
这样分开写成两个文件是一个良好的编程风格。而java将这种分格进行强制化了。也就是说,为了延续以往C/C++程序员的既定编程习惯,作为后辈的我们也得遵守。
2.类比于java中每一个java文件中都得有一个与其文件名相同的公共类,头文件和其相应的源文件有名称上的联系吗?
正如1中所言,二者完全无关。
但你会发现,在很多的IDE中,当你创建一个类的头文件时,会自动创建一个同名的源文件,这只是一种方便快捷的方式,并没有语法意义在里面。
而在java的IDE中,自动创建与文件同名的公共类就是一种基于语法的强制行为了。
从上面可以看出,关于文件的定义及组织上讲,C/C++的语法正是太过于自由了,这就造成了任何人都可以写出让人难以捉摸,让人发狂的多层引用且毫无逻辑的代码,最重要的是,它绝不会引发编译链接报错。可想而知,在C/C++下,没有规范的代码组织会引发多么大的灾难。这或许也是为什么像Python这种将缩进都定义在语法规范中的代码为什么会引起众多的追捧了吧。大家受够了完全自由,无拘无束的生活。
3.既然如此,为什么我还要写头文件和源文件。(会很长)
这样的疑问在所难免,既然本质一样,给我个理由让我费劲巴拉地去设计.h文件把!
简单来说,理由就是我想偷懒。
???傻了吧!多写个文件还偷懒了???
正经一点!:
实际上,如果代码只有100行,你确实不需要头文件。
但,如果你要写3000行的代码,里面包含100个函数呢?你也要写在一个文件里?
如果你非要这么做,确实是可以的,但显然是不合适的。
那该怎么解决呢?
我肯定想把这些函数按照一定的逻辑给分成几组,这样我以后想要修改某个函数时,别人想要阅读代码时,就方便多了。我还可以把我用到的那些全局变量也分开按我觉得正确的逻辑存放。这样,代码就不会那么乱了。
没错,就是这样。
于是我把相关的,同类型的,我自己觉得相关的函数都分开放在了同一个源文件里,可是,我只有一个main入口,我怎么从包含main入口的源文件中读到其余文件的内容呢?
头文件包起来啊。在预处理阶段,不就是简单地吧#include 包含的文件都复制到该文件吗?
是的,但这样子似乎还是不需要.h文件,因为我只需要#include那些我想要的.c/.cpp文件不就行了。
确实如此,但在复杂情况下,我们会遇到一些情况:
如果我的头文件出现如下情况:
main.cpp:
#include "a.cpp"
#include "b.cpp"而此时,在b.cpp中,也存在一个头文件包含:
b.cpp:
#include "a.cpp"而a.cpp里面有了全局变量的定义。
a.cpp:
int c;这个时候贸然编译运行main.cpp会怎么样?
会报错·!!!
为什么?因为预处理时a.cpp被复制了两次,一次是main.cpp自身复制a.cpp时,一次是在复制b.cpp时,如若a.cpp中有两次变量定义,那显然多次定义一定得报错吧,同样,函数多次同名同参定义也得报错。
一个问题,在整个编译过程中那个过程报错了呢?(关于整个编译过程分为哪些阶段可参照第2篇,
相似的问题还会在4中再次提到)在编译阶段,出现同名同类型符号,编译出错
那怎么办?
好在C/C++还可以这样:多次声明。
定义或许不能多次,但声明绝对可以。
既然是在编译阶段出了重复定义的错,我又不能不包含相关代码,那我干脆只包含相关函数,变量的声明不就行了。
注意,C/C++的编译阶段和我们认为的广义的编译生成可执行文件过程是存在区别的。具体可以看第二篇文章。
没错,作为先驱的程序员们或许也是这么想的,于是乎,我只需要把a.cpp和b.cpp中的全局变量以及函数声明都写在main.cpp,再把a.cpp中的全局变量以及函数声明都写在b.cpp不就可以了?
是的,完全正确,这样就不需要担心编译阶段因为多次定义而报错了,而且我还能在链接阶段找到相应的重定向地址。
我真是个小聪明。
到此为止,我依然没有找到从良(误)使用.h作为头文件的理由。
但你似乎隐约觉得直接把声明语句复制粘贴多份放在每个源文件的开头这种做法有损自己优雅的智商,于是乎,你想到了如下的方法:
为什么我不把这些该死的多次用到声明写在一个.h文件里,这样每次只要#include这个.h文件不就行了。
哇哇哇,怎么会有这么聪明的小可爱啊啊,
1.深入理解C/C++中头文件(.h)与源文件(.c/.cpp)以及我们为什么需要.h头文件相关推荐
- C++头文件(xxx.h)与源文件(xxx.cpp)的关系
头文件是不能被编译的: "#include"为编译预处理指令,其作用:源文件(xxx.cpp)中的#include "xxx.h"指令将xxx.h 中的代码在编 ...
- [译] 理解 NPM 5 中的 lock 文件
本文讲的是[译] 理解 NPM 5 中的 lock 文件, 原文地址:Understanding lock files in NPM 5 原文作者:Jiří Pospíšil 译文出自:掘金翻译计划 ...
- 如何理解Linux shell中的“2>1”(将文件描述2(标准错误输出)的内容重定向到文件描述符1(标准输出))(尼玛>符号竟然不支持搜索,害我搜搜不到,只能搜)
文章目录 前言 有何妙用 如何理解 总结 前言 有时候我们常看到类似这样的脚本调用: ./test.sh > log.txt 2>&1 这里的2>&1是什么意思?该如 ...
- xml文件 卷积神经网络_理解卷积神经网络中的输入与输出形状(Keras实现)
即使我们从理论上理解了卷积神经网络,在实际进行将数据拟合到网络时,很多人仍然对其网络的输入和输出形状(shape)感到困惑.本文章将帮助你理解卷积神经网络的输入和输出形状. 让我们看看一个例子.CNN ...
- ROS系列——mavros功能包中常用话题和服务介绍,包括消息名称、类型、头文件、成员变量、示例代码
ROS系列--mavros功能包中常用话题和服务介绍,包括消息名称.类型.头文件.成员变量.示例代码 官方链接 常用话题 订阅 1.1 系统状态 1.2 GPS数据 1.3 本地位置 1.4 三轴速度 ...
- 习题 8.4 在本章第8.3.3节中分别给出了包含类定义的头文件student.h,包含成员函数定义的源文件student.cpp以及包含主函数的源文件main.cpp。请完善该程序,在类中增加。。。
C++程序设计(第三版) 谭浩强 习题8.4 个人设计 习题 8.4 在本章第8.3.3节中分别给出了包含类定义的头文件student.h,包含成员函数定义的源文件student.cpp以及包含主函数 ...
- 习题 8.5 将本章的例8.4改写为一个多文件的程序:1.将类定义放在头文件arraymax.h中;2.将成员函数定义放在源文件arraymax.cpp中;3.主函数放在源文件file1.cpp中。
C++程序设计(第三版) 谭浩强 习题8.5 个人设计 习题 8.5 将本章的例8.4改写为一个多文件的程序: 1.将类定义放在头文件arraymax.h中: 2.将成员函数定义放在源文件arraym ...
- 深入理解Java虚拟机--中
深入理解Java虚拟机--中 第6章 类文件结构 6.2 无关性的基石 无关性的基石:有许多可以运行在各种不同平台上的虚拟机,这些虚拟机都可以载入和执行同一种平台无关的字节码(ByteCode),从而 ...
- C++实践小经验——#include 何时放在头文件里,何时放在cpp文件里?
结合我自己的经验,谈一谈模块化编程时#include应该出现的位置.总结起来大体有二条规则: 一.规则1:只包含必要的头文件 看下面这个模块: ===foo.c==== #include <st ...
- 全面理解.htaccess语法中RewriteCond和RewriteRule意义
全面理解.htaccess语法中RewriteCond和RewriteRule意义 .htaccess 配置文件可以通过RewriteCond 和 RewriteRule 实现伪静态. Rewrite ...
最新文章
- Java对线_新手如何通过练习打好Java基础?
- Ubuntu NEF to JPEG(Linux NEF 原生格式转jpeg)
- vue——走马灯-类轮播图
- 论文浅尝 | 二维卷积知识图谱嵌入
- python find函数 和index的区别_使用带有find和index的map时Python2和Python3之间的区别...
- 程序员们记得还是八五年PC登陆我国时候的事?
- love2d杂记4--有用的辅助库
- hdu 2883 kebab 网络流
- 如何以出售开源软件为生?
- 常用算法大全-贪婪算法
- android list嵌套list,Android开发日常-listVIiew嵌套webView回显阅读位置
- 劝人自杀情绪多变,人工智能真不是个“东西”
- autoware官方入门教使用
- 持续集成:Jenkins Pipeline共享库定义和使用
- 【论文笔记】Image Manipulation Detection by Multi-View Multi-Scale Supervision
- Stream流常用操作(超全+实例)
- 计算机安全之网络安全议论文,网络安全800字议论文
- Cortex-M入门
- 每日 CRUD?两年滴滴和入职头条的后端开发经验分享!共勉!
- 2021蓝桥c++(B组)第二场----双阶乘
热门文章
- win10应用商店恢复
- Android手机ram大小,安卓手机RAM容量演进史,如何从192MB走到16GB,HTC:我有话要说...
- python使用pypandoc将html转换成word文档
- java图片无损压缩_java无损压缩Thumbnailator(google)
- bitcoin中私钥、公钥、钱包地址之间的关系
- python随机出100道加法题_python3 随机生成10以内的加法算术题
- html编写一个飞机游戏,利用HTML5 Canvas如何制作一个简单的打飞机游戏
- HTML5之插入图片
- 读OpenCV自带的标定例程“calibration.cpp”感想
- Uplift Model