静态库&动态库的创建和使用

一. 静态库的创建

i. 创建一个空项目(也可以直接选择动态库项目,VS会自动生成框架)。
在已安装的模板中选择“常规”,在右边的类型下选择“空项目”,在名称和解决方案名称中输入staticlib。点击确定。
ii. 添加头文件和源文件
在解决方案资源管理器的头文件中添加,mylib.h文件,在源文件添加mylib.c文件(即实现文件)
iii. 在mylib.h文件中添加如下代码

   #ifndef TEST_H  
   #define TEST_H
   int myadd(int a,int b);  
   #endif

iv. 在mylib.c文件中添加如下代码:

 #include"test.h"

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

v. 配置项目属性。因为这是一个静态链接库,所以应在项目属性的“配置属性”下选择“常规”,在其下的配置类型中选择“静态库(.lib)。
在这里插入图片描述
vi. 编译生成新的解决方案,在Debug文件夹下会得到mylib.lib (对象文件库),将该.lib文件和相应头文件给用户,用户就可以使用该库里的函数了。

二.静态库的使用

  • 方法一: 配置项目属性
    i. 添加工程的头文件目录:工程—属性—配置属性—c/c+±–常规—附加包含目录:加上头文件存放目录。
    在这里插入图片描述

    ii. 添加文件引用的lib静态库路径:工程—属性—配置属性—链接器—常规—附加库目录:加上lib文件存放目录。
    在这里插入图片描述 iii. 然后添加工程引用的lib文件名:工程—属性—配置属性—链接器—输入—附加依赖项:加上lib文件名。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-USRryaEU-1610179649878)(./jpg/静态使用3.jpg)]

    iV. 引入需要的头文件

  • 方法二: 使用编译语句
    i. 参照方法一的第一步。
    ii. 添加如下语句:
    #pragma comment(lib,"./mylib.lib")
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RolCpjLD-1610179649881)(./jpg/静态使用4.jpg)]

三.静态库优缺点

  • 静态库对函数库的链接是放在编译时期完成的,静态库在程序的链接阶段被复制到了程序中,和程序运行的时候没有关系;
  • 程序在运行时与函数库再无瓜葛,移植方便。
  • 浪费空间和资源,所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。

四. 动态库创建

  • 方法一:
    i. 创建一个空项目,项目名称为mydll
    ii. 在解决方案资源管理器的头文件中添加,test.h文件,在源文件添加test.c文件(即实现文件)。
    iii. 在test.h文件中添加如下代码:
    #ifndef _TEST_H_
    #define _TEST_H_
    
    #ifdef add_EXPORTS
    #define add_EXPORT __declspec(dllexport)
    #else
    #define add_EXPORT __declspec(dllimport)
    #endif
    
    add_EXPORT int add(int x,int y);
    
      //中间一段的意思是: 如果在工程里添加预定义宏addEXPORTS 那addEXPORT 就指代__declspec(dllexport) 用于dll的导出(函数 变量 类等)
      导出变量用 __declspec(dllexport) int a;
      导出函数用 __declspec(dllexport) void foo();
      导出类用 class __declspec(dllexport) a{};
      如果应用程序需要调用dll中的函数,则需要用__declspec(dllimport)修饰,因此当工程中不包含addEXPORTS预定义时,addEXPORT 就指代__declspec(dllimport) 这样生成dll程序和调用dll的程序可以使用同一个头文件
      (ps:工程中会自动添加一个add_EXPORTS预定义,在属性—c\c++ —preprocesspr里面)
       #endif 
    
    iv. 在test.c文件中添加如下代码:
    #include "test.h"
    
    int add(int x,int y)
    {
       return x+y;
    }
    
    v. 配置项目属性。因为这是一个动态链接库,所以应在项目属性的“配置属性”下选择“常规”,在其下的配置类型中选择“动态库(.dll)。(方法同静态库)
    vi. 编译生成新的解决方案,在Debug文件夹下会得到mydll.dll (对象文件库),将该.dll文件、.lib文件和相应头文件给用户,用户就可以使用该库里的函数了。
  • 方法二:
    i. 首先创建一个空项目dll,创建头文件dll.h,再创建源文件main.cpp,再创建模块定义文件即defdllcall.def文件(这个文件添加在源文件里,跟添加源文件的方法一样,可以在右上角搜索框输入.def查找),这样创建一个动态库所需的条件就满足了。
    ii. 在头文件dll.h中写入如下代码
     #pragma once
     int _stdcall add(int x, int y);
     int _stdcall sub(int x, int y);
    
    iii. 在源文件main.c中写入如下代码
    #include "dll.h"
    int _stdcall add(int x, int y)
    {
        return x + y;
    }  
    int _stdcall sub(int x, int y)
    {
        return x - y;
    }  
    
    iv. 在模块定义文件defdllcall.def中写入如下代码
    LIBRARY "defdllcall"
    EXPORTS
    add @1
    sub @2
    
    v. 把文件配置成动态库文件,右键项目生成即可。

五. 动态库使用

  • 方法一:隐式调用
    本次是对方法一创建的动态库进行调用
    i. 创建主程序TestDll,将test.h、mydll.dll和mydll.lib复制到源代码目录下。
    ii. 在程序中指定链接引用链接库 : #pragma comment(lib,"./mydll.lib")
    iii. 引入需要的头文件,如下:
    在这里插入图片描述

  • 方法二:显式调用
    本次是对方法二创建的动态库进行调用,创建一个控制台程序,调用代码如下:

    #include <iostream>
    #include <Windows.h>
    #include "tchar.h"
    using namespace std;
    
    typedef int (WINAPI* AddFunc)(int x, int y);
    typedef int (WINAPI* SubFunc)(int x, int y);
    
    int main()
    {
       //使用_T要加头文件"tchar.h",加入动态库Dll.dll文件路径
       HMODULE hdll = LoadLibrary(_T("..\\Debug\\Dll.dll"));
       std::cout << "Hello World!\n";
       if (hdll != NULL)
       {
          AddFunc ADD1 = (AddFunc)GetProcAddress(hdll, (char*)(1));
          SubFunc SUB1 = (SubFunc)GetProcAddress(hdll, "sub");
          if (ADD1 != NULL)
          {
                cout << "显试调用add" << endl;
                cout << ADD1(3, 4) << endl;//对add函数调用
          }
          if (SUB1 )
          {
                cout << "显试调用sub" << endl;
                cout << SUB1(4,2) << endl;//对sub函数调用
          }
          FreeLibrary(hdll);
       } 
    }
    
    

    运行结果如下图:
    在这里插入图片描述

  • 在显式调用中,由于使用的是.def的方法,在获取动态库中函数地址时有两种方法
    方法一: AddFunc ADD1 = (AddFunc)GetProcAddress(hdll,(char)(1));* 这是在.def文件中add后面加@1的使用方法
    方法二: 当然也可以使用通用获取地址方法 AddFunc ADD1 = (AddFuncGetProcAddress(hdll,“add”);
    AddFunc ADD1 = (AddFunc)GetProcAddress(hdll,(char*)(1));这句(char*)(1)中1是add后面@的数字,相当于add在动态库中的地址,GetProcAddress的函数原型里1这个位置数据类型是char*类型.

  • 附.def的一些小知识:
    .def 文件中的第一条 LIBRARY 语句不是必须的,但LIBRARY 语句后面的 DLL 的名称必须正确,即与生成的 动态链接库的名称必须匹配。此语句将 .def 文件标识为属于 DLL。 链接器将此名称放到 DLL 的 导入库中。
    EXPORTS 语句列出名称,可能的话还会列出 DLL 导出函数的序号值。通过在函数名的后面加上 @ 符和一个数字,给函数分配序号值。当指定序号值时,序号值的范围必须是从 1 到 N,其中 N 是 DLL 导出函数的个数。
    LIBRARY BTREE
    EXPORTS
    Insert @1
    Delete @2
    Member @3
    Min @4
    def 文件中的注释由每个注释行开始处的分号 ( ; ) 指定。注释不能与语句共享一行,但可以在多行语句的规范间出现。

六. 动态库相关的问题

  • 疑问一:__declspec(dllexport)是什么意思?
    动态链接库中定义有两种函数:导出函数(export function)和内部函数(internal function)。 导出函数可以被其它模块调用,内部函数在定义它们的DLL程序内部使用。
  • 疑问二: 动态库的lib文件和静态库的lib文件的区别?
    eclspec(dllexport)是什么意思?
    动态链接库中定义有两种函数:导出函数(export function)和内部函数(internal function)。 导出函数可以被其它模块调用,内部函数在定义它们的DLL程序内部使用。
  • 疑问二: 动态库的lib文件和静态库的lib文件的区别?
    在使用动态库的时候,往往提供两个文件:一个引入库(.lib)文件(也称“导入库文件”)和一个DLL(.dll)文件。虽然引入库的后缀名也是“lib”,但是,动态库的引入库文件和静态库文件有着本质的区别,对一个DLL文件来说,其引入库文件(.lib)包含该DLL导出的函数和变量的符号名,而.dll文件包含该DLL实际的函数和数据。在使用动态库的情况下,在编译链接可执行文件时,只需要链接该DLL的引入库文件,该DLL中的函数代码和数据并不复制到可执行文件,直到可执行程序运行时,才去加载所需的DLL,将该DLL映射到进程的地址空间中,然后访问DLL中导出的函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值