1 分别编译与链接(Linking)
大多数高级语言都支持分别编译,程序员可以显式地把程序划分为独立的模块或文件,然后每个独立部分分别编译。在编译之后,由链接器把这些独立的片段(称为编译单元)“粘接到一起”。(想想这样做有什么好处?)
在 C/C++ 中,这些独立的编译单元包括 obj 文件(一般的源程序编译而成)、lib 文件(静态链接的函数库)、dll 文件(动态链接的函数库)等。
静态链接方式:在程序执行之前完成所有的组装工作,生成一个可执行的目标文件(EXE 文件)。
动态链接方式:在程序已经为了执行被装入内存之后完成链接工作,并且在内存中一般只保留该编译单元的一份拷贝。
2 静态链接库与动态链接库
先来阐述一下 DLL(Dynamic Linkable Library) 的概念,你可以简单的把 DLL 看成一种仓库,它提供给你一些可以直接拿来用的变量、函数或类。
静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都被直接包含在最终生成的 EXE 文件中了。但是若使用 DLL,该 DLL 不必被包含在最终的 EXE 文件中,EXE 文件执行时可以 “动态” 地引用和卸载这个与 EXE 独立的 DLL 文件。
采用动态链接库的优点:(1)更加节省内存;(2)DLL 文件与 EXE 文件独立,只要输出接口不变,更换 DLL 文件不会对 EXE 文件造成任何影响,因而极大地提高了可维护性和可扩展性。
3 认识动态链接库
动态链接是相对于静态链接而言的。所谓静态链接是指把要调用的函数或者过程链接到可执行文件中,成为可执行文件的一部分。换句话说,函数和过程的代码就在程序的 exe 文件中,该文件包含了运行时所需的全部代码。当多个程序都调用相同函数时,内存中就会存在这个函数的多个拷贝,这样就浪费了宝贵的内存资源。而动态链接所调用的函数代码并没有被拷贝到应用程序的可执行文件中去,而是仅仅在其中加入了所调用函数的描述信息(往往是一些重定位信息)。仅当应用程序被装入内存开始运行时,在 Windows 的管理下,才在应用程序与相应的 DLL 之间建立链接关系。当要执行所调用 DLL 中的函数时,根据链接产生的重定位信息,Windows 才转去执行 DLL 中相应的函数代码。一般情况下,如果一个应用程序使用了动态链接库,Win32 系统保证内存中只有 DLL 的一份复制品
动态链接库的两种链接方法:
(1) 装载时动态链接 (Load-time Dynamic Linking):这种用法的前提是在编译之前已经明确知道要调用 DLL 中的哪几个函数,编译时在目标文件中只保留必要的链接信息,而不含 DLL 函数的代码;当程序执行时,调用函数的时候利用链接信息加载 DLL 函数代码并在内存中将其链接入调用程序的执行空间中 (全部函数加载进内存),其主要目的是便于代码共享。(动态加载程序,处在加载阶段,主要为了共享代码,共享代码内存)
(2) 运行时动态链接 (Run-time Dynamic Linking):这种方式是指在编译之前并不知道将会调用哪些 DLL 函数,完全是在运行过程中根据需要决定应调用哪个函数,将其加载到内存中(只加载调用的函数进内存),并标识内存地址,其他程序也可以使用该程序,并用 LoadLibrary 和 GetProcAddress 动态获得 DLL 函数的入口地址。(dll 在内存中只存在一份,处在运行阶段)
上述的区别主要在于阶段不同,编译器是否知道进程要调用的 dll 函数。动态加载在编译时知道所调用的函数,而在运行态时则必须不知道。