提到编译我们并不陌生,静态编译、动态编译等相关概念也经常出现,那么编译到底是什么,编译的过程又是如何呢?其实我们平时提到的编译,仅仅是编译过程中的一部分,真正的编译过程可总结为如下四步:
- 预编译
- 编译
- 汇编
- 连接
预编译
删除所有#define
,展开所有宏定义;删除注释;处理#include
等等。
编译
将预处理后的文件进行一系列词法分析、语法分析以及优化等。此步骤需要.h文件,换言之,编译器需要知道,我程序所用的方法都是声明过的,都是合法的!
汇编
程序经过预编译、编译、汇编,此时已经输出目标文件(win下的.obj,Linux下的.o)。
链接
链接即是将之前生成的目标文件以及我们的库进行链接,生成可执行程序的过程,这里的可执行程序所指范围很广,可以是exe、lib、dll 等。此步骤所需文件为.lib文件以及上一步生成的目标文件,用其进行重定位!此步骤中静态编译、动态编译有很大区别,下面我们介绍下这两种模式的区别。
静态编译
链接过程
进行重定位,即在.lib文件中找到我们所需内容(并不是将所有指令拷贝)嵌入到对应目标文件中,和目标文件一起打包生成为最后的可执行程序。此时可执行程序可能会很大。
生成和使用
编译静态库仅会生成.lib文件,无需其他支持。
使用此静态库,链接过程中需要.lib文件,一旦编译完毕,即打包成最终可执行程序,运行时则无需此.lib文件。
动态编译
链接过程
.lib文件仅仅为索引文件,真正的核心内容都在dll中,链接过程仅仅用.lib文件进行重定位。个人理解此重定位是将目标文件中调用的函数(动态库中)的代码,替代为.lib里所存储的此函数在dll里的索引位置,一起打包为最后的可执行程序。这样在程序运行时,程序可以顺利的解析dll(找到相应方法在dll中的正确地址),从而使程序顺利的进行!
生成和使用
编译动态库最终生成dll,lib,exp等等文件。lib为索引文件,dll为核心指令文件,exp是一个中间文件,我们可不予理会。
使用此动态库,链接过程需要.lib文件,运行时需要dll文件。
动态导出/导入符
__declspec(dllexport)
__declspec(dllimport)
顾名思义,这俩符号是动态库导出时候才用到的。如果主程序调用静态库,包含的头文件还加__declspec(dllimport)
,这样会导致直接编译不过。
__declspec(dllexport)
这个符号非常重要,假如不加,我们动态编译后,生成的dll会有问题,而且还缺少lib和exp等文件,没有lib文件,意味着主程序想要使用这个动态库,则四部曲中的链接步骤无法进行。__declspec(dllimport)
这个符号相对于导出,就显得不重要了。这个符号会出现在供主程序包含的.h文件中,假如没有此符号,编译主程序时大多数不受影响!仅当我们的导出方法中含有static方法时,则必须加此符号,否则会导入失败。
为了使我们头文件用同一版本,我们只需定义一个通用宏替代本身导入导出符号即可,具体做法如下:
|
这样在本项目中,我们只需定义宏MOUDLE_LIBRARY
,此时MOUDLE_EXPORT
即为导出符号。当其他项目使用此库,包含其头文件时,由于没有定义此宏,则MOUDLE_EXPORT
即为导入符号。
附带提一句,在VS中,编译时关于对C++基本库的使用是动态还是静态,我们要想将其打包进我们的程序中,可以在选项中调节(MT/MD等选项)。