11、静态库vs动态库:区别、优缺点、链接方式,以及如何选择

说实话,静态库和动态库这个话题,几乎每个C++开发者都会遇到。我记得刚入行那会儿,被这两个东西搞得晕头转向——明明都是库,为什么用起来差别这么大?后来在项目里踩过几次坑,才真正搞明白它们各自的门道。

今天我就把这两者的区别、优缺点、链接方式,以及我个人的选择经验,一次性给你讲清楚。

什么是静态库?什么是动态库?

先简单说下定义。

静态库:编译时直接把库代码复制到你的可执行文件里。Windows下是.lib,Linux下是.a

动态库:编译时只记录引用信息,运行时才加载库代码。Windows下是.dll,Linux下是.so

说白了,静态库是「复制粘贴」,动态库是「随用随取」。这个比喻虽然糙,但道理没错。

核心区别对比

我习惯用一张表来对比,这样一目了然:

对比维度 静态库 动态库
链接时机 编译链接阶段 程序运行时
文件大小 可执行文件较大 可执行文件较小
部署方式 单个文件,直接运行 需附带.so/.dll文件
更新维护 需重新编译整个程序 替换库文件即可
内存占用 每个进程一份 多进程可共享
启动速度 稍慢(需加载库)

优缺点分析

静态库的优点

  • 部署简单:一个可执行文件搞定,不用操心依赖库
  • 运行稳定:不会因为系统里缺了某个库而崩溃
  • 启动快:不需要动态加载的过程

静态库的缺点

  • 体积大:每个可执行文件都包含一份库代码
  • 更新麻烦:库升级了,所有依赖它的程序都得重新编译
  • 内存浪费:多个进程用同一个库,内存里存了多份

动态库的优点

  • 节省空间:多个程序共享一份库代码
  • 更新灵活:替换库文件就行,不用重新编译程序
  • 按需加载:用到了才加载,没用就不加载

动态库的缺点

  • 部署复杂:必须带上所有依赖的库文件
  • 版本冲突:著名的「DLL地狱」就是这玩意儿搞出来的
  • 启动稍慢:运行时需要解析和加载库

核心观点:没有绝对的好坏,只有适不适合你的场景。

链接方式详解

在CMake里,链接库的方式其实很简单。我直接上代码:

静态库的创建与链接

# 创建静态库
add_library(mylib STATIC
    src/foo.cpp
    src/bar.cpp
)

# 链接静态库
target_link_libraries(myapp PRIVATE mylib)

动态库的创建与链接

# 创建动态库
add_library(mylib SHARED
    src/foo.cpp
    src/bar.cpp
)

# 链接动态库
target_link_libraries(myapp PRIVATE mylib)

你看,CMake里就一个关键字STATICSHARED的区别。但背后的行为完全不同。

小技巧:如果你不确定用哪种,可以先写成add_library(mylib STATIC),后期改成SHARED几乎不需要改其他代码。CMake对这两种库的接口是统一的。

如何选择?我的实战经验

这个问题,我在不同项目里遇到过很多次。说说我的判断标准:

选静态库的场景

  • 工具类小程序:比如命令行工具,部署越简单越好
  • 嵌入式开发:目标环境不可控,最好一个文件搞定
  • 核心算法库:不想让别人看到实现细节,静态库更安全
  • 性能敏感场景:减少运行时加载的开销

选动态库的场景

  • 大型项目:多个模块共享同一个库,节省磁盘和内存
  • 插件系统:运行时动态加载不同的功能模块
  • 频繁更新的库:比如UI库、网络库,更新时只替换dll/so
  • 第三方依赖:系统自带的库(如libc、libm)基本都是动态的

避坑指南:我曾经在一个项目里,把所有第三方库都编译成静态库。结果可执行文件直接飙到200多MB,部署到客户机器上,人家说「你这一个工具比我们整个系统还大」...后来改成动态库,体积降到30MB。嗯,从那以后我学会了权衡。

知识体系图

下面这张图,帮你理清静态库和动态库的核心逻辑:

静态库 vs 动态库 核心逻辑 源代码 编译 (Compile) 静态库 (.a / .lib) 编译时复制到可执行文件 动态库 (.so / .dll) 运行时动态加载 单个可执行文件 (大) 可执行文件 + 库文件 ✅ 部署简单 ❌ 体积大 ✅ 节省空间 ❌ 依赖管理复杂

CMake中的实战配置

最后,给你一个完整的CMake示例,展示如何同时支持静态库和动态库:

cmake_minimum_required(VERSION 3.16)
project(LibDemo VERSION 1.0.0)

# 让用户选择库类型
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)

if(BUILD_SHARED_LIBS)
    add_library(mymath SHARED
        src/add.cpp
        src/sub.cpp
    )
else()
    add_library(mymath STATIC
        src/add.cpp
        src/sub.cpp
    )
endif()

# 设置导出宏(动态库需要)
target_compile_definitions(mymath PRIVATE
    $<$<BOOL:${BUILD_SHARED_LIBS}>:MYMATH_EXPORTS>
)

# 链接到主程序
add_executable(demo main.cpp)
target_link_libraries(demo PRIVATE mymath)

这个配置里,用户可以通过-DBUILD_SHARED_LIBS=ON来切换库类型。我个人习惯在开发阶段用动态库,发布时切回静态库——这样既方便调试,又方便部署。

我的建议:如果你刚开始接触,先学会用静态库。等你对项目结构有把握了,再尝试动态库。别一上来就搞动态库,依赖问题会让你怀疑人生。

好了,静态库和动态库的核心内容就这些。记住一句话:没有银弹,只有适合。根据你的项目规模、部署环境、更新频率来选,准没错。


公众号:蓝海资料掘金营,微信deep3321