一、静态库(.lib)
1. 静态库制作
方法一:使用Visual Studio IDE
创建静态库项目(Win32 Static Library)
添加源文件(.cpp)和头文件(.h)
编译生成 .lib 文件
方法二:使用命令行
rem 编译为目标文件 cl /c mylib1.cpp mylib2.cpp rem 创建静态库 lib /OUT:mylib.lib mylib1.obj mylib2.obj2. 静态库使用
代码示例:
mylib.h
#pragma once #ifdef MYLIB_EXPORTS #define MYLIB_API __declspec(dllexport) #else #define MYLIB_API __declspec(dllimport) #endif MYLIB_API int add(int a, int b); MYLIB_API int subtract(int a, int b);main.cpp
#include "mylib.h" #include <iostream> int main() { std::cout << "3 + 5 = " << add(3, 5) << std::endl; std::cout << "10 - 4 = " << subtract(10, 4) << std::endl; return 0; }编译链接:
rem 方法1:直接链接 cl main.cpp mylib.lib rem 方法2:指定库目录 cl main.cpp /I include_path /link /LIBPATH:lib_path mylib.lib二、动态库(DLL)
1. 动态库制作
方法一:使用__declspec(dllexport)
mydll.h
#pragma once #ifdef MYDLL_EXPORTS #define MYDLL_API __declspec(dllexport) #else #define MYDLL_API __declspec(dllimport) #endif extern "C" MYDLL_API int multiply(int a, int b); extern "C" MYDLL_API double divide(double a, double b);mydll.cpp
#include "mydll.h" BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved) { switch (reason) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } MYDLL_API int multiply(int a, int b) { return a * b; } MYDLL_API double divide(double a, double b) { if (b == 0) return 0; return a / b; }方法二:使用模块定义文件(.def)
mydll.def
LIBRARY mydll EXPORTS multiply @1 divide @2编译动态库:
rem 生成DLL和导入库 cl /D MYDLL_EXPORTS /LD mydll.cpp /link /DEF:mydll.def rem 或者使用 cl /D MYDLL_EXPORTS /LD mydll.cpp /Fe:mydll.dll /link /IMPLIB:mydll.lib2. 动态库使用方式
方式一:隐式链接(最常用)
// main.cpp #include "mydll.h" #include <iostream> int main() { std::cout << "3 * 5 = " << multiply(3, 5) << std::endl; std::cout << "10 / 2 = " << divide(10, 2) << std::endl; return 0; }编译:
cl main.cpp mydll.lib方式二:显式链接(运行时加载)
#include <windows.h> #include <iostream> typedef int (*MultiplyFunc)(int, int); typedef double (*DivideFunc)(double, double); int main() { HINSTANCE hDll = LoadLibrary(TEXT("mydll.dll")); if (!hDll) { std::cerr << "无法加载DLL" << std::endl; return 1; } MultiplyFunc multiply = (MultiplyFunc)GetProcAddress(hDll, "multiply"); DivideFunc divide = (DivideFunc)GetProcAddress(hDll, "divide"); if (multiply && divide) { std::cout << "3 * 5 = " << multiply(3, 5) << std::endl; std::cout << "10 / 2 = " << divide(10, 2) << std::endl; } FreeLibrary(hDll); return 0; }3. 使用CMake构建库
CMakeLists.txt(静态库):
cmake_minimum_required(VERSION 3.10) project(MyStaticLib) add_library(mystatic STATIC src/mylib1.cpp src/mylib2.cpp ) target_include_directories(mystatic PUBLIC include)CMakeLists.txt(动态库):
cmake_minimum_required(VERSION 3.10) project(MyDynamicLib) add_library(mydynamic SHARED src/mydll.cpp ) target_include_directories(mydynamic PUBLIC include)三、主要区别和使用建议
| 特性 | 静态库 (.lib) | 动态库 (DLL) |
|---|---|---|
| 链接时机 | 编译时 | 运行时 |
| 文件大小 | 可执行文件较大 | 可执行文件较小 |
| 更新 | 需要重新编译 | 替换DLL即可 |
| 内存使用 | 每个程序单独加载 | 多程序共享 |
| 加载速度 | 快 | 相对较慢 |
| 依赖管理 | 简单 | 需确保DLL存在 |
四、实用技巧
依赖查看:使用
dumpbin /exports mydll.dll查看导出函数依赖追踪:使用 Dependency Walker 或
dumpbin /dependents program.exe版本控制:在DLL中设置版本信息(.rc文件)
调试DLL:设置调试器加载符号,或在DLL项目中设置调试命令
五、常见问题解决
找不到DLL:确保DLL在程序目录、系统目录或PATH包含的目录中
导出函数名修饰:使用
extern "C"避免C++名称修饰运行时错误:检查DLL依赖的运行时库版本(MSVCRT)
符号冲突:使用静态库时注意全局变量和函数的命名
建议根据项目需求选择:
小型工具或嵌入式环境 → 静态库
大型系统或需要插件机制 → 动态库
需要热更新功能 → 动态库 + 显式加载