文章目录
- 动态链接库和静态链接库
- 什么是链接库?
- 静态链接库
- 动态链接库
- 动态链接库的俩种链接方式
- 加载时动态链接
- 运行时动态链接
动态链接库和静态链接库
动态链接库和静态链接库都是共享代码的方法,只是二者略有区别。
以C/C++为例,一个可执行文件的生成主要包括预编译、编译、汇编和链接。而静态链接和动态链接就是 在链接阶段的俩种处理。
什么是链接库?
关于代码复用,有些文件专门用于存储可以重复使用的代码块,例如功能实用的函数或者类,我们通常将它们称为库文件,简称“库”(Library)。将这种库文件进行打包编译后得到二进制文件就是链接库。
链接库是一个不能独立运行的二进制文件,它必须经过其他程序调用,才可以载入内存中。
根据链接方式的不同,可以分为静态链接库和动态链接库
静态链接库
所谓静态链接,就是在程序执行前,将所有目标文件同静态链接库一起组织成可执行文件,这样生成的可执行文件可以独立运行。
采用静态链接库的方式共享代码有一个明显的缺点,那就是文件的体积会很大,因为可执行文件包含了所有目标文件和静态链接库的数据。这样容易造成内存空间的浪费。同时,不利用代码的模块化:如果有某个模块需要更新,整个程序都需要重新连接才能运行。
若是有多个程序调用相同函数,内存中就会存在这个函数的多个拷贝。
动态链接库
动态链接是相对静态链接而言的,动态链接所调用的代码并没有被打包到可执行文件中,被拷贝的往往只是某些函数的描述信息(如重定位信息),只有当程序执行的过程中,需要调用到动态库中的函数式,动态链接库中的函数才会被载入内存中。
一般情况下,一个程序如果使用了动态链接库,系统会保证内存中只有一份DLL的复制品。
动态链接库可以随可执行文件一同载入内存,也可以在可执行文件运行过程中载入,即可执行文件什么时候需要,动态链接库才会载入内存。
采用动态链接库方便程序的更新,当程序的某个模块更新后,只需要将旧的模块替换掉,程序运行时会自动将所有模板载入内存并动态地链接在一起。
但是动态链接库也有一定的缺点,静态链接生成的可执行文件能够在其他同类操作系统上直接运行。但是如果是动态链接生成的文件,在移植到其他操作系统上后,需要连同该可执行文件所调用到的DLL文件一并拷贝过去,不然不能保证程序的正常运行。
动态链接库的俩种链接方式
动态链接实际上还有俩种不同的连接方式:加载时动态链接和运行时动态链接(隐式加载和显式加载)
加载时动态链接
在加载时动态链接中,应用程序像本地函数一样显式调用导出的 DLL 函数。要使用加载时动态链接,请在编译和链接应用程序时提供头文件 (.h) 和导入库 (.lib) 文件。执行此操作时,链接器将为系统提供加载 DLL 所需的信息,并在加载时解析导出的 DLL 函数位置。
使用加载时动态链接,同静态链接有一个相同的缺点,那就是如果程序的体积稍大,程序开始时加载的时间就会过长。
加载时动态链接和静态链接的区别:
- 链接的时机:加载时动态链接是在程序加载时程序才会将动态库载入到内存中,而静态链接则是在编译的时候就已经将静态库的代码和数据嵌入到可执行文件中了。
正如上面所说的,使用加载时动态链接,需要提供头文件 (.h) 和导入库 (.lib) 文件。可以直接在源码中引入.lib文件。
例如:
#pragma comment(lib, "dllDemo.lib")
为了更好的模块化设计,也可以将lib中所要用到函数声明放在头文件中。
例如:
//dllDemo.h
#ifndef _DLLDEMO_H
#DEFINE _DLLDEMO_H
#pragma comment(lib,"dllDemo.lib")
_declspec(dllexport) int add(int, int);
_declspec(dllexport) int sub(int, int);
#endif
之后的主程序中记得
#include "dllDemo.h"
上述代码还用了_declspec(dllimport)
标识符声明函数来自动态链接库。
运行时动态链接
在运行时动态链接中,应用程序调用LoadLibrary
函数或LoadLibraryEx
函数在运行时加载DLL。 DLL成功加载后,可以使用GetProcAddress
函数获取要调用的导出DLL函数的地址。当您使用运行时动态链接时,不需要导入库文件。
LoadLibrary
函数的作用是将指定的模块加载到调用进程的地址空间中。
函数定义:
HMODULE LoadLibraryA(
[in] LPCSTR lpLibFileName //模块的名称。这可以是库模块(.dll 文件)也可以是可执行模块(.exe文件)
);
如果调用成功,将会返回该模块的句柄。
当得到该模块的句柄后,可以使用GetProcAddress
函数,它从指定的动态链接库 (DLL) 检索导出函数(也称为过程)或变量的地址。
函数原型:
FARPROC GetProcAddress(
[in] HMODULE hModule, //该模块的句柄
[in] LPCSTR lpProcName //函数或变量的名称
);
如果调用成功,则返回导出函数或变量的地址。
示例:
#include <windows.h>
int main() {
// 加载动态链接库
HMODULE hLibrary = LoadLibrary("example.dll");
if (hLibrary != NULL) {
// 获取函数地址
FARPROC functionAddress = GetProcAddress(hLibrary, "exampleFunction");
if (functionAddress != NULL) {
// 调用动态链接库中的函数
typedef void (*FunctionType)();
FunctionType myFunction = (FunctionType)functionAddress;
myFunction();
}
// 卸载动态链接库
FreeLibrary(hLibrary);
}
return 0;
}
以上关于运行时动态链接的实例是基于Windows的,如果是在linux上使用运行时动态链接,则需要通过使用 dlopen
和 dlsym
以及 dlclose
函数。
实例:
#include <dlfcn.h>
int main() {
// 加载动态链接库
void* libraryHandle = dlopen("libexample.so", RTLD_LAZY);
if (libraryHandle != NULL) {
// 获取函数地址
void (*myFunction)() = (void (*)())dlsym(libraryHandle, "exampleFunction");
if (myFunction != NULL) {
// 调用动态链接库中的函数
myFunction();
}
// 卸载动态链接库
dlclose(libraryHandle);
}
return 0;
}
参考
https://learn.microsoft.com/en-us/troubleshoot/windows-client/deployment/dynamic-link-library
https://c.biancheng.net/dll/what_is_library.html
https://blog.csdn.net/fuzhongmin05/article/details/54616520
https://blog.csdn.net/u010154760/article/details/45689899?spm=1001.2014.3001.5502