news 2026/6/15 19:02:44

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/15 14:18:14

target_include_directories对比 PUBLIC / PRIVATE

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、表述详解核心结论补充核心定义&#xff08;关键&#xff09; 二、完整项目示例1. 项目目录结构2. 源码文件include/lib.h&#xff08;库的公共接口&#xff09;…

作者头像 李华
网站建设 2026/6/9 16:22:23

本文档将依次阐述门禁CPU卡读卡器、IC卡梯控读头的技术规格、接口定义、通信协议解析、开发指南及故障排查流程,为后续选型的技术细节展开奠定专业基础。保留原始设计参数,未经过简化确保专业人员直接上手用

全协议读卡器模块技术文档 引言:本文档详细介绍了全协议读卡器模块的技术规范与应用方案。该模块支持ISO14443A/B、ISO15693等主流协议&#xff0c;兼容MIFARE、NFC等多种卡片类型&#xff0c;具备RS485和Wiegand双接口。核心参数包括&#xff1a;工作电压DC5V0.2V&#xff0c;…

作者头像 李华
网站建设 2026/6/15 18:52:36

穷举算法:最基础直观的暴力搜索算法

文章目录一、简介1、简介2、缺点3、优化技巧二、经典案例1、百钱买百鸡&#xff08;经典多变量穷举&#xff09;2、查找指定范围内的质数&#xff08;单变量穷举 验证优化&#xff09;3、简单数字密码破解&#xff08;固定长度穷举&#xff09;4、数组中两数之和等于目标值&am…

作者头像 李华
网站建设 2026/6/14 17:51:20

拼多多 最新 anti-content 分析

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 逆向分析 部分python代码 cp execjs…

作者头像 李华
网站建设 2026/6/15 15:02:20

【JPCS出版,有ISSN号,高录用,EI稳检索,福州大学、青岛大学威海创新研究院联合主办,Fellow报告,会议有保障】2026年能源、电力与可持续发展国际学术会议(EESD 2026)

2026年能源、电力与可持续发展国际学术会议&#xff08;EESD 2026&#xff09; 2026 International Conference on Energy, Electricity and Sustainable Development 2026年3月6-8日 中国昆明&#xff08;可线上参会&#xff09; JPCS出版&#xff01;有ISSN号&#xff0c…

作者头像 李华
网站建设 2026/6/15 18:55:02

【广东工业大学主办,SAE出版,EI快速稳定检索,学术大咖加盟 | 低空经济、交通系统、机器制造、供应链网络、无人机等主题均可投递】 2026年低空经济与技术应用国际学术会议 (LETA 2026)

2026年低空经济与技术应用国际学术会议 (LETA 2026) 2026 International Conference on Low - altitude Economy and Technology Application 低空经济、交通系统、机器制造、技术应用、供应链网络、无人机等主题均可投递 SAE出版&#xff01;EI&SCOPUS快速稳定检索&…

作者头像 李华