环境
windows + visual studio 2019
visual studio 2019创建cmake工程
1. 静态库.lib
1.1 静态库编译生成
以下是我创建的cmake工程文件结构,只关注高亮文件夹部分
- libout 存放编译生成的.lib文件
- libsrc 存放编译用的源代码和头文件
- CMakeLists.txt 此次编译CMake项目的配置文件
接着我们看一下我们的代码, 代码注释很详细
libscr/add.h 头文件中声明了两个不同精度的求和函数
cpp
// #ifndef... #define... #endif... 防止头文件被重复调用的时候头文件中多次定义报错, 意思只定义一次
#ifndef ADD_H
#define ADD_H
int add(int, int);
double add(double, double);
#endif
libscr/add.cpp 源文件是两个不同精度的求和函数的定义
cpp
// add.cpp
#include "add.h"
// 重载 int 类型的加法
int add(int a, int b) {
return a + b;
}
// 重载 double 类型的加法
double add(double a, double b) {
return a + b;
}
CMakeLists.txt
- add_library()中 addlib 是生成的.lib名称;STATIC 指定的是生成库类型为静态库
- set_target_properties 设置静态库存放路径为libout文件夹下
bash
# CMakeList.txt: 顶层 CMake 项目文件,在此处执行全局配置
# 并包含子项目。
#
cmake_minimum_required (VERSION 3.8)
project ("libgen")
# 设置c++标准
set(CMAKE_CXX_STANDARD 11)
# 生成静态库并指定静态库存放路径
add_library(addlib STATIC libsrc/add.cpp)
set_target_properties(addlib PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/libout)
全部重新生成,即可将编译的.lib文件生成到指定的文件夹下
1.2 静态库使用
同样只关注高亮文件夹部分
- libinclude 存放头文件,直接从libscr 将头文件copy过来即可
- libout 1.1中编译生成的.lib文件
- libuse 调用lib的代码实现
- CMakeLists.txt 此次编译执行的CMake项目的配置文件
libuse/main.cpp 调用静态库中的add求和函数实现
cpp
#include "add.h"
#include <iostream>
int main() {
int a = 3, b = 5;
std::cout << "add(3.1, 4.1) = " << add(3.1, 4.1) << std::endl;
std::cout << "add(3.1f, 4.1f) = " << add(3.1f, 4.1f) << std::endl;
std::cout << "add(3, 4) = " << add(a, b) << std::endl;
return 0;
}
CMakeLists.txt 注释说的非常清楚了,不在详说
bash
# CMakeList.txt: 顶层 CMake 项目文件,在此处执行全局配置
# 并包含子项目。
#
cmake_minimum_required (VERSION 3.8)
project ("libgen")
# 设置c++标准
set(CMAKE_CXX_STANDARD 11)
# 生成静态库并指定静态库存放路径
# add_library(addlib STATIC libsrc/add.cpp)
# set_target_properties(addlib PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/libout)
## 创建可执行文件并指定头文件
add_executable(testlib libuse/main.cpp)
## 指定addlib.lib的绝对路径
target_link_libraries(testlib PRIVATE ${PROJECT_SOURCE_DIR}/libout/addlib.lib)
## 指定头文件目录
target_include_directories(testlib PRIVATE ${PROJECT_SOURCE_DIR}/libinclude)
执行结果和预期完全一样
2. 动态库.dll
2.1 动态库编译生成
- dllout 存放编译生成的动态库文件
- dllsrc 存放编译用的源代码和头文件
- CMakeLists.txt 此次编译CMake项目的配置文件
dllscr/add.h 这里与静态库非常不一样
在Windows平台上,动态库(DLL)的导出和导入需要通过 __declspec(dllexport)
和 __declspec(dllimport)
来显式声明。通常,我们会使用一个宏来切换这两种声明。如下:
- 当
MATHLIBRARY_EXPORTS
被定义时,MATHLIBRARY_API
会被替换为__declspec(dllexport)
,表示当前正在编译动态库,需要导出符号。在编译dll的时候CMakeLists.txt中会定义。 - 当
MATHLIBRARY_EXPORTS
未被定义时,MATHLIBRARY_API
会被替换为__declspec(dllimport)
,表示当前正在使用动态库,需要导入符号。在执行调用dll的时候CMakeLists.txt中不会定义。
cpp
// add.h
#pragma once
#ifdef MATHLIBRARY_EXPORTS
#define MATHLIBRARY_API __declspec(dllexport)
#else
#define MATHLIBRARY_API __declspec(dllimport)
#endif
extern "C" MATHLIBRARY_API int add(int a, int b);
extern "C"
:告诉编译器按照C语言的方式处理函数名,即不进行名称修饰。- 当
MATHLIBRARY_EXPORTS
被定义时,MATHLIBRARY_API
会被替换为__declspec(dllexport)
,表示该函数需要从动态库中导出。 - 当
MATHLIBRARY_EXPORTS
未被定义时,MATHLIBRARY_API
会被替换为__declspec(dllimport)
,表示该函数是从动态库中导入的。
dllscr/add.cpp 源文件函数定义没什么好说的
cpp
// add.cpp
#include "add.h"
extern "C" MATHLIBRARY_API int add(int a, int b) {
return a + b;
}
CMakeLists.txt
- add_library()中 adddll 是生成的.lib名称;SHARED 指定的是生成库类型为动态库
- set_target_properties 设置静态库存放路径为dllout文件夹下
- target_compile_definitions() 编译动态库是一定要定义宏,告知此时需要导出库
bash
cmake_minimum_required(VERSION 3.10)
project("dllgen")
# 设置C++标准
set(CMAKE_CXX_STANDARD 11)
# 添加动态库
add_library(adddll SHARED dllsrc/add.cpp)
# 指定动态库的导出宏 编译动态库的时候定义宏MATHLIBRARY_EXPORT
target_compile_definitions(adddll PRIVATE MATHLIBRARY_EXPORTS)
# 设置输出路径
set_target_properties(adddll PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/dllout
RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/dllout
)
编译生成的只需要.dll 和.lib 。记住.lib并非静态库,而是导入库,用于解析符号引用
2.2 动态库使用
同样只关注高亮文件夹部分
- dllinclude 存放头文件,直接从dllscr 将头文件copy过来即可
- dllout 2.1中编译生成的.dll, .lib文件
- dlluse 调用dll的代码实现
- CMakeLists.txt 此次编译执行的CMake项目的配置文件
dlluse/main.cpp 调用静态库中的add求和函数实现
cpp
// main.cpp
#include <iostream>
#include "add.h"
int main() {
int result = add(3, 4);
std::cout << "3 + 4 = " << result << std::endl;
return 0;
}
CMakeLists.txt 注释说的非常清楚了,不在详说,但是需要注意的是:
- 链接的是.lib 导入库
- .dll 的文件夹路径一定要添加到环境变量中,或者放在.exe目录下,我这里是提前添加到环境变量中去了
bash
cmake_minimum_required(VERSION 3.10)
project("dllgen")
# 设置C++标准
set(CMAKE_CXX_STANDARD 11)
# 添加动态库
#add_library(adddll SHARED dllsrc/add.cpp "dllinclude/add.h")
# 指定动态库的导出宏 编译动态库的时候定义宏MATHLIBRARY_EXPORT
#target_compile_definitions(adddll PRIVATE MATHLIBRARY_EXPORTS)
# 设置输出路径
#set_target_properties(adddll PROPERTIES
# ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/dllout
# RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/dllout
#)
# 添加可执行文件
add_executable(testdll dlluse/main.cpp)
# 链接动态库的导入库到可执行文件,并非直接链接动态库
target_link_libraries(testdll PRIVATE ${PROJECT_SOURCE_DIR}/dllout/adddll.lib)
## 指定头文件目录
target_include_directories(testdll PRIVATE ${PROJECT_SOURCE_DIR}/dllinclude)
执行结果和预期完全一样,而且输出视图中我们能够看到adddll.dll的加载
3 静态库、动态库区别联系(以下来自deepseek)
静态库和动态库是两种常见的代码复用方式,它们在代码链接、内存管理、维护灵活性等方面有显著差异。以下是它们的核心区别与联系:
一、核心区别
特性 | 静态库 | 动态库 |
---|---|---|
文件格式 | .lib (Windows)、.a (Linux) |
.dll (Windows)、.so (Linux) + 导入库 .lib (Windows) |
链接时机 | 编译时直接嵌入到可执行文件中 | 运行时由操作系统动态加载到内存中 |
代码存储 | 可执行文件包含库代码的完整副本 | 可执行文件仅包含对动态库的引用 |
内存占用 | 多进程无法共享库代码,内存冗余 | 多进程可共享同一动态库,节省内存 |
更新维护 | 更新库需重新编译整个程序 | 更新库只需替换动态库文件,无需重新编译程序 |
运行依赖 | 无需外部库文件,独立性强 | 依赖动态库文件存在且路径正确 |
加载速度 | 启动快(代码已嵌入) | 启动稍慢(需加载动态库) |
体积大小 | 可执行文件体积大 | 可执行文件体积小 |
二、核心联系
-
代码复用
- 两者均用于封装通用功能(如数学计算、文件操作),避免重复开发。
- 例如:将加密算法封装为库,供多个项目调用。
-
模块化设计
- 通过库分离核心模块和业务逻辑,提升代码可维护性。
- 例如:将图形渲染功能封装为动态库,独立于主程序更新。
-
混合使用
- 项目中可同时使用静态库和动态库。
- 例如:主程序链接静态库保证核心功能稳定,插件使用动态库实现热更新。
三、典型应用场景
场景 | 静态库 | 动态库 |
---|---|---|
嵌入式开发 | ✅ 资源有限,需减少运行时依赖 | ❌ 动态加载可能增加复杂性 |
跨平台插件 | ❌ 更新需重新编译 | ✅ 支持运行时加载,如浏览器插件、游戏模组 |
系统级共享功能 | ❌ 内存占用高 | ✅ 如Windows的kernel32.dll ,Linux的libc.so |
闭源分发 | ✅ 代码完全嵌入,保护知识产权 | ❌ 需分发动态库文件 |
快速迭代更新 | ❌ 每次更新需重新编译 | ✅ 替换动态库即可生效 |
四、技术细节对比
1. 符号冲突处理
- 静态库:多个静态库中同名符号会导致链接错误。
- 动态库:符号在运行时解析,不同动态库的同名符号可共存。
2. 跨平台兼容性
- Windows :
- 静态库:
.lib
- 动态库:
.dll
(实现文件) +.lib
(导入库)
- 静态库:
- Linux :
- 静态库:
.a
- 动态库:
.so
(直接链接,无需导入库)
- 静态库:
3. CMake配置
cmake
# 静态库
add_library(MyStaticLib STATIC src.cpp)
target_include_directories(MyStaticLib PUBLIC include)
# 动态库(需导出符号)
add_library(MySharedLib SHARED src.cpp)
target_compile_definitions(MySharedLib PRIVATE MYLIB_EXPORTS)
target_include_directories(MySharedLib PUBLIC include)
# 使用库
add_executable(MyApp main.cpp)
target_link_libraries(MyApp PRIVATE MyStaticLib MySharedLib)
五、如何选择?
-
选静态库 :
✅ 项目规模小,无需频繁更新
✅ 对启动速度敏感(如命令行工具)
✅ 目标环境依赖管理困难(如嵌入式设备)
-
选动态库 :
✅ 代码需多进程共享(如系统库)
✅ 支持热更新或插件化架构
✅ 减少可执行文件体积(如大型应用)
六、总结
静态库和动态库本质是代码复用的两种策略:
- 静态库追求简单性和独立性,牺牲体积和灵活性。
- 动态库追求资源共享和灵活性,牺牲启动速度和环境依赖。
实际开发中,二者常结合使用。例如:核心基础模块用静态库保证稳定性,业务模块用动态库支持灵活扩展。