news 2026/6/9 23:30:45

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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 自动宏的特性
  • 总结

一、静态库无需导出符号的核心原因

底层原理

  1. 静态库本质
    静态库是编译后的目标文件(.obj)的打包归档,它本身不是可独立运行的程序。
  2. 链接机制
    生成可执行文件(.exe)时,链接器会将静态库中被用到的代码/数据直接复制到最终的.exe内部,属于静态链接
  3. 符号可见性
    Windows 平台下,静态库的全局函数、变量默认全部公开可见,链接器可以直接解析所有符号,不需要__declspec(dllexport)这类导出修饰符
  4. 关键区分:两种.lib文件
    VS 中会出现两种后缀都是.lib的文件,作用完全不同:
    库类型文件说明链接阶段行为运行时依赖
    静态库纯代码归档文件代码复制进.exe无额外依赖
    动态库配套导入库(索引文件)仅记录符号地址,不复制代码必须依赖.dll

二、动态库(DLL):头文件不写导出符号不行

核心规则

Windows 平台的DLL(动态链接库)有严格的符号隔离机制:
默认情况下,DLL 内的所有全局符号都是隐藏的,不会自动暴露给外部程序
如果不做显式导出声明,会直接导致:

  1. DLL 编译成功,但不会生成导出表
  2. 外部程序链接时,报无法解析的外部符号错误;
  3. 无法调用 DLL 中的函数。

补充对比:Linux/macOS 的动态库(.so/.dylib)默认导出所有符号,无需手动声明;但 Windows 为了性能和安全性,强制要求显式导出,这是平台特性差异。

动态库导出符号的两种标准方案

方案1:使用__declspec修饰符(VS 最常用、推荐)

配合宏封装,实现编译DLL时导出、调用DLL时导入的自动切换,这是工业级标准写法。

方案2:使用模块定义文件(.def

无需修改代码,通过配置文件声明导出符号,适合兼容老项目。


三、完整示例:基于加法函数的动态库实现

基于你之前的代码,改造为Windows 动态库标准格式,适配 VS + CMake:

1. 目录结构

cmake_demo/ ├── include/ │ └── lib.h # 带导出宏的头文件 ├── src/ │ ├── lib.cpp # 动态库实现 │ └── main.cpp # 调用方程序 └── CMakeLists.txt

2. 核心代码修改

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++名字改编extern"C"{// 修饰函数:标记为导出/导入符号MYLIB_APIintadd(inta,intb);}#endif// LIB_H

src/lib.cpp(实现文件,无需修改修饰符)

#include"lib.h"// 实现函数,无需额外修饰intadd(inta,intb){returna+b;}

src/main.cpp(调用方代码,无修改)

#include<stdio.h>#include"lib.h"intmain(){intres=add(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 环境)

  1. 用 CMake 生成 VS 解决方案,编译工程;
  2. 输出目录会生成两个关键文件:mylib.dll(运行时库)、mylib.lib(导入库);
  3. 直接运行main_app.exe,可正常调用 DLL 中的add函数。

5. 测试「不写导出符号」的失败场景

如果删除头文件中的MYLIB_API修饰符,直接声明函数:

// 错误写法:无导出修饰符extern"C"intadd(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的依据,无需手动定义。


总结

  1. 静态库(.lib:无需任何导出符号,全局符号默认公开,链接时代码直接嵌入.exe,这是你能直接调用函数的原因;
  2. Windows 动态库(.dll必须显式导出符号,不写导出修饰符会导致链接失败,推荐用__declspec(dllexport/dllimport)+ 宏封装的标准写法;
  3. 两种.lib文件切勿混淆:静态库是代码归档,动态库配套的.lib仅为符号索引(导入库)。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/7 11:47:23

Swinject 在移动端广告系统中的应用

Swinject 在移动端广告系统中的应用关键词&#xff1a;Swinject、移动端广告系统、依赖注入、组件化、解耦摘要&#xff1a;本文主要探讨了 Swinject 在移动端广告系统中的应用。首先介绍了 Swinject 的背景知识&#xff0c;包括其核心概念和工作原理。接着通过具体的代码示例&…

作者头像 李华
网站建设 2026/5/31 6:02:50

视觉检测中的数字光纤放大器的核心参数和调整

在当今快速发展的工业自动化领域&#xff0c;机器视觉系统已成为质量控制、自动化生产和智能检测的关键技术。根据国际机器人联合会&#xff08;IFR&#xff09;2024年发布的《全球工业自动化报告》&#xff0c;全球机器视觉市场规模预计在2025年达到180亿美元&#xff0c;年复…

作者头像 李华
网站建设 2026/6/8 21:20:09

【通信】基于DPCM编码及2DPSK调制数字频带通信系统Matlab仿真

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。&#x1f34e; 往期回顾关注个人主页&#xff1a;Matlab科研工作室&#x1f447; 关注我领取海量matlab电子书和…

作者头像 李华
网站建设 2026/6/4 8:07:26

【信号调制】使用不同的分类器(逻辑回归分类器、决策树、随机森林、全连接密集层和CNN)来训练模型,以预测不同信噪比值下信号的调制类型附Python代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。&#x1f34e; 往期回顾关注个人主页&#xff1a;Matlab科研工作室&#x1f34a;个人信条&#xff1a;格物致知,完整Matlab代码及仿真咨询…

作者头像 李华
网站建设 2026/5/23 15:34:42

【修正-高斯拉普拉斯滤波器-用于平滑和去噪】基于修正高斯滤波拉普拉斯地震到达时间自动检测研究附Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。&#x1f34e; 往期回顾关注个人主页&#xff1a;Matlab科研工作室&#x1f34a;个人信条&#xff1a;格物致知,完整Matlab代码及仿真咨询…

作者头像 李华