• 静态链接库与动态链接库


    静态链接库

    何为静态链接库?

    win平台下xxx.lib文件即静态链接库。静态库只有lib文件,该文件包含了函数代码本身,在编译时会直接将代码完整添加到程序之中。

    使用VS2019创建一个静态库项目

    在这里插入图片描述
    在这里插入图片描述
    选择静态库,在下面选项中勾选空项目

    在这里插入图片描述
    testlib.h

    #ifndef TESTLIB_H
    #define TESTLIB_H
    
    int add(int a, int b);
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    testlib.cpp

    #include "testlib.h"
    
    int add(int a, int b) 
    {
    	return a + b;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    生成解决方案并生成静态库

    在这里插入图片描述
    接下来尝试使用静态库,在同一解决方案下新建项目test,并右击将其设置为启动项目

    在这里插入图片描述
    test.cpp

    #include <stdio.h>
    
    int main()
    {
    	int a, b = 0;
    
    	scanf_s("%d %d", &a, &b);
    
    	int c = add(a, b);
    
    	printf("%d", c);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    直接编译,报错

    在这里插入图片描述
    那是因为程序不知道add函数是什么,或者说不知道去哪里调用

    包含一下静态库项目中的头文件testlib.h

    #include <stdio.h>
    #include "../testlib/testlib.h"
    
    int main()
    {
    	int a, b = 0;
    
    	scanf_s("%d %d", &a, &b);
    
    	int c = add(a, b);
    
    	printf("%d", c);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    可通过编译,但运行继续报错

    在这里插入图片描述
    那是因为头文件中只有声明,没有实现。因此,可以引出静态库如何调用的问题

    静态库的调用需要两个文件:xxx.hxxx.lib

    方式一:将testlib.lib文件复制至test项目工作目录,并添加附加依赖项

    在这里插入图片描述
    在这里插入图片描述

    #include <stdio.h>
    #include "../testlib/testlib.h"
    
    // #pragma comment(lib, "testlib.lib")
    
    int main()
    {
    	int a, b = 0;
    
    	scanf_s("%d %d", &a, &b);
    
    	int c = add(a, b);
    
    	printf("%d", c);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    方式二:test项目中添加静态库的库目录相对路径,并在程序中添加导入对应库的代码

    在这里插入图片描述

    #include <stdio.h>
    #include "../testlib/testlib.h"
    
    #pragma comment(lib, "testlib.lib")
    
    int main()
    {
    	int a, b = 0;
    
    	scanf_s("%d %d", &a, &b);
    
    	int c = add(a, b);
    
    	printf("%d", c);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    方式三:test项目中添加静态库的库目录相对路径,并在程序中添加附加依赖项

    在这里插入图片描述
    在这里插入图片描述

    #include <stdio.h>
    #include "../testlib/testlib.h"
    
    // #pragma comment(lib, "testlib.lib")
    
    int main()
    {
    	int a, b = 0;
    
    	scanf_s("%d %d", &a, &b);
    
    	int c = add(a, b);
    
    	printf("%d", c);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    可见,无论是哪种方式,都要使test项目找得到静态库。实际开发中方式二使用居多。

    方式三与方式二的区别在于:方式二使用预处理指令导入库文件;而方式三通过设置工程属性导入库文件。

    #pragma comment(lib, "XXX.lib")
    
    • 1

    动态链接库

    windows操作系统有三个核心的动态链接库:

    • kernel32.dll

    kernel32.dllWindows中非常重要的32位动态链接库文件,属于内核级文件。它控制着系统的内存管理、数据的输入输出操作和中断处理,当Windows启动时,kernel32.dll就驻留在内存中特定的写保护区域,使别的程序无法占用这个内存区域。

    Windows系统中,每个.exe文件在双击打开时都会加载kernel32.dll这个系统模块,该模块中有一个LoadLibrary()函数,可以将DLL文件加载到自身进程中。可以用CreateRemoteThread()函数创建一个远程线程,让目标进程调用LoadLibrary()来加载我们自己写的DLL

    • user32.dll

    user32.dllWindows用户界面相关应用程序接口,用于包括Windows处理,基本用户界面等特性,如创建窗口和发送消息。

    • gdi32.dll

    gdi32.dllWindows GDI图形用户界面相关程序,包含的函数用来绘制图像和显示文字

    何为动态链接库?

    win平台下xxx.dll即为动态链接库。动态链接库不会将完整代码拷贝进xxx.exe文件,仅仅会在程序运行的时候,才会将具体的代码加载进去。

    使用VS2019创建一个动态库项目

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    testdll.h

    #ifndef TESTDLL_H
    #define TESTDLL_H
    
    #ifdef _DLLAPI
    	#define DLLAPI _declspec(dllexport)
    #else
    	#define DLLAPI _declspec(dllimport)
    #endif
    
    int DLLAPI add(int a, int b);
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这里使用条件编译指令是更加规范的做法,可以让testdll项目和测试项目test共用一个头文件testdll.h

    设置testdll项目的预处理器中添加宏_DLLAPI

    在这里插入图片描述
    如此一来,可以使得在生成dll文件时将DLLAPI替换成_declspec(dllexport),而在测试项目中导入头文件时,将DLLAPI替换成_declspec(dllimport)

    testdll.cpp

    #include "testdll.h"
    
    int add(int a, int b)
    {
    	return a + b;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    右击项目,重新生成解决方案并生成动态库(注意,dll项目不能调试,点击调试会报错的)

    在这里插入图片描述

    在这里插入图片描述

    此时,我们去下载一个工具 dllexp,打开软件并导入testdll.dll,可见函数名被修饰了

    在这里插入图片描述

    接下来尝试使用动态库,在同一解决方案下新建项目test,并右击将其设置为启动项目

    动态库调用有两种方式:静态调用动态调用

    1. 静态调用

    静态调用需要三个文件:xxx.hxxx.libxxx.dll

    使用相对路径设置test项目的库目录路径

    在这里插入图片描述

    test.cpp

    #include <stdio.h>
    #include "../testdll/testdll.h"
    
    #pragma comment(lib, "testdll.lib") //这个不是真正的静态库
    
    int main()
    {
    	int a = 1;
    	int b = 2;
    
    	int c = add(a, b);
    
    	printf("%d\n", c);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    为什么不需要如网上其他帖子所说:在调用dll文件时,需要xxx.hxxx.libxxx.dll缺一不可。而我这里没有将dll文件复制到test项目的可执行目录中,是因为他们俩同处在一个解决方案下面。

    在这里插入图片描述
    但是神奇的是,test.exe可执行文件不在原项目test/Debug中,而是跑到了testdll/Debug中。纵然满足exedll文件在同一目录下,但仍然令我费解。

    在这里插入图片描述

    在这里插入图片描述
    2. 动态调用

    动态调用只要一个文件:xxx.dll

    由于testdll项目中是用cpp文件写的,即以C++的方式导出动态库的函数,那么编译器为了实现C++的函数重载会在函数导出时对函数名进行修饰。因此,修改一下testdll.h文件。

    #ifndef TESTDLL_H
    #define TESTDLL_H
    
    #ifdef _DLLAPI
    #define DLLAPI _declspec(dllexport)
    #else
    #define DLLAPI _declspec(dllimport)
    #endif
    
    // extern "C" 编译时用C的编译方式
    extern "C" DLLAPI int add(int a, int b);
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    重新生成解决方案后的dll文件中add函数名就不会被修饰

    在这里插入图片描述

    test.cpp

    #include <stdio.h>
    #include <Windows.h>
    //#include "../testdll/testdll.h"
    
    //#pragma comment(lib, "testdll.lib") //这个不是真正的静态库
    
    typedef int (*pAdd)(int a, int b);
    
    int main()
    {
    	HMODULE hDll = LoadLibrary(L"../testdll/Debug/testdll.dll"); //加载库文件
    
    	if (hDll == NULL)
    	{
    		printf("加载DLL文件失败\n");
    		return 0;
    	}
    
    	pAdd testAdd = (pAdd)GetProcAddress(hDll, "add");
    
    	int a = 1;
    	int b = 2;
    
    	printf("add(a, b) = %d\n", testAdd(a, b));
    
    	FreeLibrary(hDll);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    成功运行

    在这里插入图片描述

  • 相关阅读:
    ik分词器
    [单片机框架][bsp层][N32G4FR][bsp_pwm] pwm配置和使用
    Kafka一个节点挂掉,导致服务不可消费
    Linux防火墙
    SpringMVC (3)—拦截器
    MYSQL_
    zeek学习(五)—— 会话建立
    《动手学深度学习》(八) -- 多尺度标检测和单发多框检测
    MyBatis学习笔记(2022-11-30)
    读《大话数据结构》溢彩加强版
  • 原文地址:https://blog.csdn.net/Star_ID/article/details/125394726