.h/.cpp/.hpp/.c/.o/.obj/.lib/.dll/.a/.so/.exe傻傻分不清

这些都是c/c++编译运行时期所产生的文件,作为一个没用过这门语言的人很容易搞混。

简单分类

简单分三类,也就是三个过程需要的文件

.h/.cpp/.hpp/.c:代码源文件

.o/.obj/.lib/.a:编译过程需要的文件

.dll/.so/.exe:运行过程需要的文件

源代码

cpp 与 c

.cpp.c分别为c++和c的源代码文件。早期都只用.c文件,后来为了便于区分就为c++产生了.cpp文件。但这也只是约定俗成的,还有可能将c++源文件命名为其他后缀,例如.cxx

h(头文件)

假如A.cpp需要引入B.cpp中的方法,则需要在A.cpp的头部引入#include "B.cpp"。其实这样就可以了。但更为常见的做法是将B.cpp中的所有方法声明都提取出来单独写到一个B.h文件中,需要用B.cpp中的方法时,只需要引入B.h即可。这很类似java中的接口模式,但不管是实践还是思想都是完全不同的

为什么要这样做?

  • 有的时候依赖包的源码并不是公开的,依赖提供者只会提供已经编译好的.lib文件与其对应的.h文件,此时若需要调用lib中的方法就需要先引入该头文件。
  • 事实上 include 关键字仅仅作用与编译期,即将include的文件原封不动地复制到本文件。所以假如被引用的头文件中声明了一些常量,你又在本地声明了一样的常量,这个问题就能在编译器就被发现。

hpp

个人理解和.cpp文件功能上一样,都是讲方法声明和实现写在一个文件中,但又在性能上有优化。

注:hpp=header plus plus,意思是将实现写进头文件中,概念上还是属于头文件,但看起来像cpp文件。

每个.cpp文件都会被单独编译成一个.o文件,然后再通过链接步骤将它们链接在一起。但hpp文件不会单独编译

上面说 include 其实就是将代码原封不动复制过来,include的hpp文件会和源文件一起只生成一个.o文件,减少了编译次数

编译

.o 与 .obj

它们都是源文件编译后的产物,类似于java文件编译后产生的class文件

不同点在于 .obj 是在windows下的编译产物,.o 是linux下的。

.lib 与 .a

每个cpp文件都会被单独编译成.o文件。假如它们之间有依赖关系(例如A.cpp引用了B.cpp中的方法),那么它们怎么找到相应的方法实现呢(因为此时A.cpp只引用了B.h,该文件中只有方法声明,没有实现)?

所以在编译结束后还需要一个链接操作,即将调用方法的符号引用替换成真实的方法地址。然后将它们一起打包成一个.lib(windows下).a(linux下)文件,最终生成一个.exe文件。

注:此时又有静态链接和动态链接的概念,见下文中“运行”部分

运行

静态链接

将整个项目所有的源码及依赖全都打包在一起,此时整个项目没有任何外部依赖,直接打开就能运行,这种打包方式称为静态链接

例如上述“编译”过程最后,将所有的文件都打包进一个.lib文件中,运行过程中若需要找方法体,直接在该lib文件中就能找到。

静态链接的好处是兼容性强,不受外部环境的干扰。因为所有的依赖它都已经自带了,可以自力更生了。

缺点是包比较大,同样是因为它携带的东西太对了。

动态链接

有的依赖确实不需要随身携带,例如cout这种方法的依赖,一般系统下都有其实现并且不会轻易去更改。此时就可以使用动态链接。动态链接不打包方法体,只打包方法声明和调用地址。

例如上述“编译”过程的最后,打包成的.lib文件中仅仅包含被调用方法的声明,此时如果使用这些方法,程序就会在运行过程中动态地去系统中寻找依赖库文件,即.dll(windows下)或.so(linux下)文件。

这也是为什么一些软件运行过程中会突然报却是某种dll的错误

动态链接的优点是打包的体积小,但缺点也很明显,对环境依赖高。一旦环境中没有所依赖的动态库软件就无法运行。

.dll 与 .exe

dll只是一个依赖包,就好比是一个工具箱。

exe就是在上述dll中加入了一个main方法,使得其可以直接执行。

总结

lib和dll总结

如果是静态链接,lib类似于自家的厨房,自己想吃什么就做什么,所有的材料也都是充沛的。此时lib就是所有方法的声明加实现。

如果是动态链接,lib就类似于餐厅的公共菜单,想吃什么直接点就行,不用去管怎么做。但不同的餐厅可能做法不同。且可能你点的菜某家餐厅没有,那就会报材料缺失的错误。此时lib相当于只是方法的一个索引了。

Leave a Comment