CMake制作动态库与静态库对比

📅 发布时间:2026/7/8 22:35:59 👁️ 浏览次数:
CMake制作动态库与静态库对比
提示文章写完后目录可以自动生成如何生成可参考右边的帮助文档文章目录一、静态库无需导出符号的核心原因底层原理二、动态库DLL头文件不写导出符号**不行**核心规则动态库导出符号的两种标准方案方案1使用 __declspec 修饰符VS 最常用、推荐方案2使用模块定义文件.def三、完整示例基于加法函数的动态库实现1. 目录结构2. 核心代码修改include/lib.h带导出/导入宏封装src/lib.cpp实现文件无需修改修饰符src/main.cpp调用方代码无修改3. CMakeLists.txt编译动态库4. 编译与运行VS 环境5. 测试「不写导出符号」的失败场景补充方案使用 .def 文件导出符号四、关键补充说明1. 关于 extern C 的作用2. 动态库运行依赖3. CMake 自动宏的特性总结一、静态库无需导出符号的核心原因底层原理静态库本质静态库是编译后的目标文件.obj的打包归档它本身不是可独立运行的程序。链接机制生成可执行文件.exe时链接器会将静态库中被用到的代码/数据直接复制到最终的.exe内部属于静态链接。符号可见性Windows 平台下静态库的全局函数、变量默认全部公开可见链接器可以直接解析所有符号不需要__declspec(dllexport)这类导出修饰符。关键区分两种.lib文件VS 中会出现两种后缀都是.lib的文件作用完全不同库类型文件说明链接阶段行为运行时依赖静态库纯代码归档文件代码复制进.exe无额外依赖动态库配套导入库索引文件仅记录符号地址不复制代码必须依赖.dll二、动态库DLL头文件不写导出符号不行核心规则Windows 平台的DLL动态链接库有严格的符号隔离机制默认情况下DLL 内的所有全局符号都是隐藏的不会自动暴露给外部程序。如果不做显式导出声明会直接导致DLL 编译成功但不会生成导出表外部程序链接时报无法解析的外部符号错误无法调用 DLL 中的函数。补充对比Linux/macOS 的动态库.so/.dylib默认导出所有符号无需手动声明但 Windows 为了性能和安全性强制要求显式导出这是平台特性差异。动态库导出符号的两种标准方案方案1使用__declspec修饰符VS 最常用、推荐配合宏封装实现编译DLL时导出、调用DLL时导入的自动切换这是工业级标准写法。方案2使用模块定义文件.def无需修改代码通过配置文件声明导出符号适合兼容老项目。三、完整示例基于加法函数的动态库实现基于你之前的代码改造为Windows 动态库标准格式适配 VS CMake1. 目录结构cmake_demo/ ├── include/ │ └── lib.h # 带导出宏的头文件 ├── src/ │ ├── lib.cpp # 动态库实现 │ └── main.cpp # 调用方程序 └── CMakeLists.txt2. 核心代码修改include/lib.h带导出/导入宏封装为了兼容 C 名字粉碎问题添加extern C通过宏区分编译库和使用库的场景#ifndefLIB_H#defineLIB_H// 宏定义编译DLL时MYLIB_EXPORT 等价于 __declspec(dllexport)// 调用方使用时MYLIB_EXPORT 等价于 __declspec(dllimport)#ifdef_WIN32#ifdefMYLIB_SHARED_EXPORTS#defineMYLIB_API__declspec(dllexport)#else#defineMYLIB_API__declspec(dllimport)#endif#else// 非Windows平台无需导出修饰符#defineMYLIB_API#endif// 兼容C调用防止C名字改编externC{// 修饰函数标记为导出/导入符号MYLIB_APIintadd(inta,intb);}#endif// LIB_Hsrc/lib.cpp实现文件无需修改修饰符#includelib.h// 实现函数无需额外修饰intadd(inta,intb){returnab;}src/main.cpp调用方代码无修改#includestdio.h#includelib.hintmain(){intresadd(10,20);printf(10 20 %d\n,res);return0;}3. CMakeLists.txt编译动态库关键用SHARED声明动态库CMake 会自动定义MYLIB_SHARED_EXPORTS宏cmake_minimum_required(VERSION 3.15) project(AddDllDemo) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 核心SHARED 表示编译为 Windows 动态库DLL add_library(mylib SHARED src/lib.cpp) target_include_directories(mylib PUBLIC include) # 生成可执行文件 add_executable(main_app src/main.cpp) # 链接动态库 target_link_libraries(main_app PRIVATE mylib)4. 编译与运行VS 环境用 CMake 生成 VS 解决方案编译工程输出目录会生成两个关键文件mylib.dll运行时库、mylib.lib导入库直接运行main_app.exe可正常调用 DLL 中的add函数。5. 测试「不写导出符号」的失败场景如果删除头文件中的MYLIB_API修饰符直接声明函数// 错误写法无导出修饰符externCintadd(inta,intb);编译后DLL 可正常生成但无导出符号链接main_app时VS 直接报错无法解析的外部符号 add程序无法运行。补充方案使用.def文件导出符号如果不想修改代码可以新建mylib.def文件手动声明导出符号LIBRARY mylib EXPORTS add 1在 CMake 中添加配置target_sources(mylib PRIVATE mylib.def)此时代码中无需任何导出修饰符链接器会根据.def文件导出符号同样可以正常调用。四、关键补充说明1. 关于extern C的作用C 编译器会对函数名做名字粉碎Name Mangling导致外部调用时符号不匹配添加extern C会强制使用 C 语言的符号命名规则保证跨调用约定兼容。2. 动态库运行依赖静态库编译后.exe可独立运行动态库编译后必须将xxx.dll放在.exe同级目录否则运行时会报错「找不到依赖库」。3. CMake 自动宏的特性当你用add_library(xxx SHARED ...)时CMake 会自动生成XXX_EXPORTS宏这也是我们封装MYLIB_API的依据无需手动定义。总结静态库.lib无需任何导出符号全局符号默认公开链接时代码直接嵌入.exe这是你能直接调用函数的原因Windows 动态库.dll必须显式导出符号不写导出修饰符会导致链接失败推荐用__declspec(dllexport/dllimport) 宏封装的标准写法两种.lib文件切勿混淆静态库是代码归档动态库配套的.lib仅为符号索引导入库。