C++动态库和静态库的生成和使用

DLL的生成

头文件

#ifndef TEST_H
#define TEST_H

#include<iostream>
#include<stdexcept> 
using namespace std;

#ifndef TESTAPI
#define TESTAPI __declspec(dllexport)
#else
#define TESTAPI __declspec(dllimport)
#endif 

extern "C" {
	TESTAPI int add(int a, int b);

	TESTAPI int sub(int a, int b);

	TESTAPI int mul(int a, int b);

	TESTAPI int div_(int a, int b);
}

#endif // ! TEST_H

为什么用TESTAPI宏呢,便于后续使用接口

可以看到此时dllimport是灰色的,导出接口dllexport生效

源文件

#include"test.h"

int add(int a, int b) {
	return a + b;
}

int sub(int a, int b) {
	return a - b;
}


int mul(int a, int b) {
	return a * b;
}

int div_(int a, int b) {
    try {
        if (b == 0) {
            throw std::runtime_error("Division by zero error!"); // 抛出异常
        }
        return a / b;
    }
    catch (const std::runtime_error& e) {
        cout << "Error: " << e.what() << endl; // 捕获并处理异常
        return 0; // 或根据需求返回其他值
    }
}

配置类型DLL

生成DLL

DLL的调用法一

把生成的test1.dll放在测试工程test_useDll的目录下

调用DLL中的接口

#include <iostream>
#include <windows.h>  
using namespace std;

// 声明函数指针
typedef int(*AddFunc)(int, int);
typedef int(*SubFunc)(int, int);
typedef int(*MulFunc)(int, int);
typedef int(*DivFunc)(int, int);

int main() {
    // 加载DLL
    HINSTANCE hDLL = LoadLibraryA("test1.dll");
    if (!hDLL) {
        cout << "Failed to load DLL." << endl;
        return 1;
    }

    // 获取函数地址
    AddFunc add = (AddFunc)GetProcAddress(hDLL, "add");
    SubFunc sub = (SubFunc)GetProcAddress(hDLL, "sub");
    MulFunc mul = (MulFunc)GetProcAddress(hDLL, "mul");
    DivFunc div_ = (DivFunc)GetProcAddress(hDLL, "div_");

    if (!add || !sub || !mul || !div_) {
        cout << "Failed to get function address." << endl;
        FreeLibrary(hDLL);
        return 1;
    }

    // 调用DLL中的函数
    int a = 10, b = 5;
    cout << "add(a, b) = " << add(a, b) << endl;
    cout << "sub(a, b) = " << sub(a, b) << endl;
    cout << "mul(a, b) = " << mul(a, b) << endl;
    cout << "div_(a, b) = " << div_(a, b) << endl;

    // 释放DLL
    FreeLibrary(hDLL);

    return 0;
}

输出结果

DLL的调用法二

  1. 新建文件夹:E:\Desktop\test_DLL
  2. 在test_DLL下面新建include文件夹
  3. 在test_DLL下面新建lib文件夹

生成的test1.lib放到lib文件夹

test.h放到include文件夹

测试工程配置

配置宏

可以看到此时dllexport是灰色的,dllimport生效

包含目录配置

库目录配置

链接器配置

将DLL与exe放置在同一目录

实际项目中所生成的DLL如果依赖了别的DLL,需要把依赖的DLL也放在同一目录下。

正常输出

静态库lib的生成

头文件改动

  1. 头文件用条件编译,源文件不动
  2. 在生成 DLL 时定义 BUILD_DLL,在使用 DLL 时定义 USE_DLL,生成静态库时都不定义。
#ifndef TEST_H
#define TEST_H

#include<iostream>
#include<stdexcept> 
using namespace std;

#ifdef BUILD_DLL
#define TESTAPI __declspec(dllexport)
#elif defined(USE_DLL)
#define TESTAPI __declspec(dllimport)
#else
#define TESTAPI // 静态库时为空
#endif


extern "C" {
	TESTAPI int add(int a, int b);

	TESTAPI int sub(int a, int b);

	TESTAPI int mul(int a, int b);

	TESTAPI int div_(int a, int b);
}

#endif // ! TEST_H

工程导出配置

生成静态库lib

静态库lib的调用

  1. 新建文件夹:E:\Desktop\test_lib
  2. 在test_lib下面新建include文件夹,放test.h
  3. 在test_lib下面新建lib文件夹,可以看到这个lib和之前生成DLL时的lib大小不一样,其实DLL生成时一起生成的lib是动态库的导入库,不是真正的静态库。

包含目录配置

库目录配置

链接器配置

正常输出

补充知识

VS生成动态库的时候会生成一个lib,这不是静态库,和单独生成的静态库lib大小差很多 。

区别说明:

类型后缀作用说明
动态库.dll包含实际的可执行代码在运行时被加载使用
导入库.lib
(与DLL配套)
用于链接时解析符号编译器链接用,运行时还需要 .dll
静态库.lib
(独立生成)
包含实际实现代码链接时被合并到可执行文件中,运行时无需 .dll

为什么大小差很多?

导入库 .lib 只是一个轻量级的“符号索引文件”,里面只包含函数/类等符号的信息,不包含实际实现代码,因此比静态库小得多。而静态库 .lib 是将编译后的对象代码封装在里面,通常会大很多。

动态库的动态加载和静态加载

上面的DLL的调用法二就是静态加载,法一就是动态加载

特性静态加载(使用导入库)动态加载(使用 LoadLibrary)
是否需要 .lib是(导入库)
是否需要 .dll
加载时机程序启动时加载 DLL程序运行时手动加载 DLL
函数使用方式像普通函数直接用必须用 GetProcAddress
获取函数指针
错误检查编译/链接时能检查符号是否存在运行时出错才知道
是否灵活不灵活(固定依赖)很灵活(可以按需加载)

动态库和静态库的区别

项目静态库(Static Library)动态库(Dynamic Library)
文件扩展名.lib
(Windows) .a
(Linux)
.dll
(Windows) .so
(Linux)
链接方式编译时合并到 EXE 或 DLL 中程序运行时加载
是否独立可独立运行,无需库文件必须依赖 DLL 文件存在
占用体积程序体积较大(复制代码)程序体积较小(共享代码)
更新方式更新需要重新编译主程序可单独更新 DLL
调用方式直接调用函数通过导入库或动态加载调用
多程序共享不行(每个程序各自有一份)可以多个程序共享一个 DLL
运行效率启动快,运行略快(无函数指针开销)启动略慢,运行略慢(调用时需跳转)

动态库和静态库的使用场景

静态库适用场景(Static Library)

🛠 1. 简单项目 / 内部工具

  • 不想处理运行时依赖(如 .dll 丢失)
  • 工具或命令行程序,小巧独立

📦 2. 单文件部署要求

  • 例如嵌入式开发、单机部署,不能有外部依赖

🔐 3. 源码闭合封装

  • 不想暴露库的实现(不易逆向)
  • 所有代码编译进可执行文件

⚡ 4. 性能优先

  • 启动更快、调用无跳转(微小优化)
  • 无运行时装载开销

✅ 动态库适用场景(Dynamic Library)

🧩 1. 插件/模块系统

  • 比如浏览器插件、游戏引擎模块
  • 支持运行时加载和替换模块

🔁 2. 多程序共享库

  • 一个 DLL 被多个程序调用,节省内存和磁盘空间
  • 例如操作系统 API、数据库驱动等

🚀 3. 支持热更新

  • 只更换 DLL 就能修复 bug 或添加功能,无需重新编译主程序

🧪 4. 版本兼容 / 动态扩展

  • 支持不同 DLL 版本(如数据库接口、第三方 SDK)
  • 加载成功与否可动态判断,适合做可选功能

💻 5. 第三方库分发

  • 如 OpenCV、SQLite、Qt 提供 DLL 供调用
  • 避免源代码或静态库暴露

✅ 实战建议

项目类型建议使用
命令行工具、小工具静态库,部署简单
插件架构(如浏览器、IDE)动态库,可按需加载
跨多个产品共享模块动态库,节省资源
面向外部发布的 C++ SDK两者都提供(常见做法)
嵌入式系统静态库,避免外部依赖

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值