动态库
-
动态库(如Windows的dll或Linux的so文件)在运行时加载,并可被多个程序共享。
-
Windows还包含与dll配套的lib文件导入库用于链接时解析DLL符号引用,不包含执行代码,而是包含指向DLL中相应符号的引用。(区别于静态库的 .lib 文件)
导入导出
__declspec(dllexport)
和__declspec(dllimport)
- 主要用于Windows平台上动态链接库(DLL)的静态加载场景。
- 静态加载使用,动态加载通常不使用这些关键字,因为动态加载涉及运行时的函数地址获取。
- 这些关键字可以用于导出和导入函数、全局变量、对象实例和类(包括成员函数)。
#ifdef EXPORTING_DLL
#define DECLSPEC __declspec(dllexport)
#else
#define DECLSPEC __declspec(dllimport)
#endif
DECLSPEC void MyFunction();
静态库
- 静态库(如Windows的lib或Unix的a文件)在编译时链接到应用程序,每个应用程序都包含了其代码副本。
动态库的加载和链接方式:
静态加载(隐式链接):
- 程序在编译时链接到动态库的导入库(.lib 文件),但不包含动态库的代码。
程序启动时,操作系统自动加载对应的动态库(.dll 或 .so 文件)。
动态加载(显式链接):
- 程序在运行时使用特定的系统调用(如Windows的 LoadLibrary 或Unix/Linux的 dlopen)动态地加载动态库。
通过 GetProcAddress(Windows)或 dlsym(Unix/Linux)来获取库中定义的函数或变量的地址。
HMODULE hModule = LoadLibrary("libraryname.dll");
if (hModule != NULL) {
// 获取函数指针
typedef void (*FunctionType)();
FunctionType func = (FunctionType)GetProcAddress(hModule, "functionname");
if (func != NULL) {
// 调用函数
func();
}
// 卸载DLL
FreeLibrary(hModule);
}
编译指令:
- 在源代码中使用如 #pragma comment(lib, “libraryname.lib”) 的编译指令(特定于Microsoft Visual C++),以自动指示链接器在链接时包括指定的库。
#pragma comment(lib, "library")
构建系统配置:
- 在使用构建系统(如CMake)时,在构建脚本中指定动态库的链接需求。
target_link_libraries(my_program PRIVATE mydll)
静态库的链接方式:
编译时链接:
- 编译器在编译程序的时候将静态库(如 .lib 或 .a 文件)中使用到的代码直接链接到可执行文件中。
只有被实际使用的代码段(符号)会被包含进最终的可执行文件中。
链接器设置:
- 通过链接器命令行参数显式指定静态库文件,如在GCC中使用 -l(指定库名)和 -L(指定库文件搜索路径)。
gcc -o main main.o -L/path/to/library -lmath
项目配置:
- 在IDE(如Visual Studio)中设置项目属性,将静态库包含到链接器的输入中。
其它
链接库
- 链接库是编译时或运行时用于解析程序中外部符号引用的代码和数据集合,可以是静态库或动态库。
extern “C”
- 用于告诉C++编译器以C的规则(也就是没有名称修饰)来处理函数
- 动态库和静态库使用
- 链接C库到C++程序中
确保了C++程序能够链接和调用C库中的函数
extern "C" {
#include "clibrary.h"
}
- 提供C接口的C++库
为库函数提供一个C接口
// C++库的头文件
#ifdef __cplusplus
extern "C" {
#endif
void my_c_interface_function();
#ifdef __cplusplus
}
#endif
- 创建跨语言的接口
当你在设计一个可供多种编程语言使用的API时,使用extern "C"可以创建一个不依赖于C++特有特性的接口。这使得用其他语言编写的程序更容易链接到你的库中。
- 加载和使用动态链接库(DLL)
当使用动态链接库(特别是用C编写的DLL),在C++中加载和调用DLL中的函数时,通常也需要使用extern "C"来保证正确的链接。
extern "C" {
DLL_EXPORT void MyFunction(); // 假设DLL是用C编写的
}
- 回调函数
如果你的C++程序需要向C语言编写的库传递回调函数,那么这些回调函数需要用extern "C"来声明
extern "C" {
void callback_function(int arg);
}
调用约定:
__cdecl
、__stdcall
、__fastcall
、__vectorcall
、__thiscall
、__pascal
、__clrcall
等,它决定了参数如何被压入到栈上以及由谁清理栈(调用者还是被调用者)__cdecl
是C和C++的默认调用约定(在Linux系统中也是默认的)。WINAPI
是__stdcall
调用约定的宏定义,用于指定Windows API函数的标准调用约定。- 创建一个DLL时,如果你在DLL内部的函数声明中指定了一个特定的调用约定(如 __stdcall 或 __cdecl),那么在使用这个DLL的应用程序中,当你声明这些函数的原型时,必须使用与DLL中相同的调用约定。
DLL中的函数声明:
__declspec(dllexport) void __stdcall SomeFunction(int param);
使用DLL相应的声明:
__declspec(dllimport) void __stdcall SomeFunction(int param);