程序从原始文件到可执行文件过程
步骤 | 原始程序 | 预编译 | 编译 | 汇编 | 链接 |
---|---|---|---|---|---|
文件 | fun.cpp、fun.h | fun.u | fun.s | fun.o | fun.exe |
预编译
- 展开头文件
- 宏替换
- 去掉注释
- 条件编译(#ifdef 、#endif …)
编译
将代码转换为汇编代码以及1、编译器在每个文件中保存一个函数地址符表,该表保存着该文件中各函数的地址;2、在这步要生成汇编代码,调用函数的代码会被编译为一条call指令,call后面跟着jmp指令的汇编代码地址,jmp后面跟着的才是“被调用函数汇编成的汇编代码的第一条指令”的地址,给call指令后面补上地址是在链接的时候做的。(调用函数:call -> jmp->函数的第一条指令(链接时才补上地址))
汇编
将汇编转换为机器码
链接
编译器将多个.o文件链接到一起生成可执行程序.exe,在这个过程中将call指令后面的地址补上;方式是从当前文件的函数地址符表开始找、扎到填上,没找到再继续向别的文件地址符表找,如果找不到,则链接失败。
静态链接库(linux: .a \ win: .lib)
之所以称为 静 态 库 静态库 静态库,是因为在链接过程会将汇编生成的目标文件.o和引用用到的库一起链接打包到可执行文件中。因此对应的链接方式为静态链接。试想一下,静态库与汇编生成的目标文件一起链接为可执行文件,那么静态库必定跟.o文件格式相似。其实一个静态库可以简单看成是一组目标文件(.o/.obj文件)的集合,即很多目标文件经过压缩打包后形成的一个文件。静态库特点总结:
- 静态库对函数库的链接是在编译时期完成的
- 程序运行时与函数库再无瓜葛,移植方便
- 缺点:浪费空间和资源,因为相关的目标文件和牵扯的函数库被链接合成了一个可执行文件
动态链接库(linux: .so \ win : .dll)
1、静态链接库不可避免的会产生空间浪费(见下图)
2、此外,还有一个问题就是静态库对于程序的更新、部署和发布都会带来麻烦。如果静态库更新了,所有使用他的应用程序都要重新编译、发布给用户(对于用户而言可能只是一个小改动,却导致整个程序要重新下载)
而动态库在程序编译时并不会连接到目标代码中,而是在程序运行时才被载入。不同的应用程序如果调用相同的库,那么在内存中只需要一份该共享库的实例,避免了空间浪费的行为。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。
动态库的特点:
- 动态库把对函数的链接过程推迟到程序运行的时期
- 可以进行进程之间的资源共享
- 程序升级变得简单
缺点:使用动态链接库的应用程序不是自完备的,1、它依赖的DLL模块也要存在,如果使用载入时动态链接,程序启动时发现DLL不存在,系统将终止程序并给出错误信息。而使用运行时动态链接,系统不会终止,但由于DLL中的导出函数不可用,程序会加载失败;2、速度比静态链接慢。当某个模块更新后,如果新模块与旧的模块不兼容,那么那些需要该模块才能运行的软件,统统死掉。