C++ 静态库与动态库的生成和使用:基于 VS Studio 生成 newmat 矩阵库的静态库与动态库

33 篇文章 1 订阅

Part.I Introduction

本文将详细地介绍 C++ 动态库和静态库,尽量让读者对他们有一个明晰的区分。然后基于一个实例(newmat 矩阵库)进行实际操作,以加深印象。

在这里插入图片描述

Chap.I 预备知识

在阅读下面的内容之前,首先需要了解如下概念和信息:

  • 动态链接库(Dynamic Link Library)或叫共享库(Shared Object)(这就是 dllso 文件后缀的由来)
  • 静态链接库(Static Link Library

有关库的一些文件

  • *.h 文件:C++ 头文件(文本文件),一般会包含函数的声明。
  • *.lib 文件:库文件(二进制文件),它可能是完整的静态库,里面有函数代码本身,在编译时直接将代码加入程序当中,应用程序直接使用;也有可能是动态库的导出声明,只包含头部信息。里面只有函数所在的 DLL 文件和文件中函数位置的入口,代码由运行时加载在进程空间中的 DLL 提供
  • *.dll 文件:动态库文件(二进制文件),Windows 下的动态库文件。
  • *.a 文件:UNIX 下的静态库文件
  • *.so 文件:UNIX 下的动态库文件

Chap.II 静态库与动态库区分

我们写程序的时候会需要加载库,一般需要先include头文件,然后再调用库函数,而库又分为两种,静态库(lib)和动态库(dll),那么这两种库有什么区别呢?

  • 静态库:我们的程序在链接时会把用到的静态库全部都链接进去,形成一个exe,这也导致我们的exe很大(程序是先编译,再链接库,最后形成exe
  • 动态库:程序在链接时在不再把整个库都链接进去,而是程序在运行过程中,用到哪个库,再加载哪个库,这就降低了exe的大小,但同时,运行速度也会变慢。

动态库与静态库优缺点分析
动态链接库优点 包括可减少程序的磁盘空间占用、方便更新库文件、共享库文件、提高程序间的互操作性、降低内存占用;缺点 包括需要确保运行环境中库文件的可用性。
静态链接库优点 包括编译后的可执行文件相对独立、移植性好、提高程序运行速度;缺点 包括每个可执行文件都包含一份静态库的拷贝、需要手动更新库文件。


静态链接库的使用

需要的文件:

  • *.h :头文件*.h中有函数的声明,使用静态链接库的项目需要引用(#include)文件才能编译通过
  • *.lib:包含了实际执行代码、符号表等等

加载*.lib的方法:

  • 用编译链接参数或者 VS 的配置属性来设置
  • 使用 pragma 编译语句,例如 pragma comment(lib,"a.lib")

动态链接库的使用——隐式调用

需要的文件

  • *.h :头文件*.h中有函数的声明,使用静态链接库的项目需要引用(#include)文件才能编译通过
  • *.lib:包含了函数所在的 *.dll 文件和文件中函数位置的信息。
  • *.dll:包含了实际执行代码、符号表等等

*.lib文件是『链接』时用的,加载方法同样有:

  • 用编译链接参数或者 VS 的配置属性来设置
  • 使用 pragma 编译语句,例如 pragma comment(lib,"a.lib")

*.dll文件是程序『运行』时用的,链接了lib之后形成的EXE可执行文件中已经有了dll的信息,所以只要dll放在和exe同一个目录下就可以了,运行时根据 EXE 需要自动加载dll中的函数。


动态链接库的使用——显示调用

需要的文件:只有动态链接库的 *.dll 文件,不需要*.h 文件和*.lib 文件。因为 LoadLibrary 之后可以使用 getProcAddress 来查找一个函数的地址从而调用该函数。


PS: 显式调用的前提是使用者需要知道想调用的函数的名字、参数、返回值信息,也就是说虽然编译链接用不上.h头文件,但是调用者编程时可能还是要看.h文件作参考来知道函数名字、参数、返回值信息


显式调用动态库步骤

  1. 创建一个函数指针,其指针数据类型要与调用的 DLL 引出函数相吻合。
  2. 通过 Win32 API 函数 LoadLibrary() 显式的调用 DLL,此函数返回 DLL 的实例句柄。
  3. 通过 Win32 API 函数 GetProcAddress() 获取要调用的 DLL 的函数地址,把结果赋给自定义函数的指针类型。
  4. 使用函数指针来调用 DLL 函数。
  5. 最后调用完成后,通过 Win32 API 函数 FreeLibrary() 释放DLL 函数。

下面将使用newmat矩阵库为例,基于 VS Studio 平台,详细介绍生成静态库和动态库的整个过程。newmat 戳我下载~

Part.II 静态库的生成与使用 (newmat)

Chap.I 生成静态库

1、新建一个『静态库』项目,名字取为LibNewmat_a

在这里插入图片描述

2、将默认创建的4个文件给排除掉,将 newmat 的 36 个文件复制到项目所在目录/src文件夹中。(注意x64)并将它们添加到项目中:右键项目→添加→现有项→进入srcCtrl+A全选→添加

在这里插入图片描述

3、选中项目右键→属性→C/C++→预编译头→不使用预编译头→应用→确定

在这里插入图片描述

4、选中项目右键→属性→C/C++→预处理器→预处理器定义→下拉三角编辑→加入_CRT_SECURE_NO_WARNINGS(这是针对newmat)进行的操作

在这里插入图片描述

5、快捷键 F6 生成,在x64/Debug目录下就生成了我们需要的*.lib

在这里插入图片描述

Chap.II 使用静态库

1、创建一个空项目,名字叫做test_a,添加一个cpp文件test.cpp

2、选中项目右键→属性→VC++ 目录→包含目录 把头文件所在目录贴进去;库目录把*.lib所在目录贴进去(看完下面的再决定要不要这样操作

在这里插入图片描述

PS:最好不要在这里加包含目录和库目录,这里时全局的(我是为了截一个图,懒狗一个)。下面是比较合适的操作:
包含目录(头文件所在目录):右键『属性』→『C/C++』→『常规』→『附加包含目录』
库目录(lib 文件所在目录):右键『属性』→『链接器』→『常规』→『附加库目录』

3、将Part.IV__Chap.I test.cpp的内容复制到文件test.cpp

4、快捷键F5得到运行结果

在这里插入图片描述

Part.III 动态库的生成与使用 (newmat)

Chap.I 生成动态库

1、新建一个『动态库』项目,名字取为LibNewmat_so

在这里插入图片描述

2、将默认创建的4个文件给排除掉,将 newmat 的 36 个文件复制到项目所在目录/src文件夹中。(注意x64)并将它们添加到项目中:右键项目→添加→现有项→进入srcCtrl+A全选→添加

在这里插入图片描述

3、选中项目右键→属性→C/C++→预编译头→不使用预编译头→应用→确定

在这里插入图片描述
4、选中项目右键→属性→C/C++→预处理器→预处理器定义→下拉三角编辑→加入_CRT_SECURE_NO_WARNINGS(这是针对newmat)进行的操作

在这里插入图片描述
5、快捷键 F6 生成,在x64/Debug目录下就生成了我们需要的*.dll

在这里插入图片描述

Chap.II 使用动态库

基于上面的操作我们可以看到:只生成了dll文件,没有生成lib文件,这是因为 newmat 库本身没有导出 (__declspec(dllexport)) 任何方法、类等,所以生成的 DL L不需要 lib 文件来记载导出符号。那这种情况下只能显示调用了,如何操作呢?浅试了一下,不会比较复杂的显式调用,暂时放弃。所以本小节后面的部分不必看了


1、创建一个空项目,名字叫做test_so,添加一个cpp文件test.cpp。(注意x64

在这里插入图片描述

2、将Part.IV__Chap.II test.cpp的内容复制到文件test.cpp

如何使用 dll 中的类型呢?求大佬指点!!

Part.IV 文件内容

Chap.I test.cpp (静态库)

/// \ingroup newmat
///@{

/// \file nm_ex1.cpp
/// Very simple example 1.
/// Invert a 4 x 4 matrix then check the result


#define WANT_STREAM       // include iostream and iomanipulators


#include "newmatap.h"     // newmat advanced functions
                          // should not be required for this example
                          // included because it seems to help MS VC6
                          // when you have namespace turned on

#include "newmatio.h"     // newmat headers including output functions


#pragma comment(lib,"LibNewmat_a.lib")
#ifdef use_namespace
using namespace RBD_LIBRARIES;
#endif


int my_main()                  // called by main()
{
    Tracer tr("my_main ");      // for tracking exceptions

    // declare a matrix
    Matrix X(4, 4);

    // load values row by row
    X.row(1) << 3.7 << -2.1 << 7.4 << -1.0;
    X.row(2) << 4.1 << 0.0 << 3.9 << 4.0;
    X.row(3) << -2.5 << 1.9 << -0.4 << 7.3;
    X.row(4) << 1.5 << 9.8 << -2.1 << 1.1;

    // print the matrix
    cout << "Matrix X" << endl;
    cout << setw(15) << setprecision(8) << X << endl;

    // calculate its inverse and print it
    Matrix Y = X.i();
    cout << "Inverse of X" << endl;
    cout << setw(15) << setprecision(8) << Y << endl;

    // multiply X by its inverse and print the result (should be near identity)
    cout << "X * inverse of X" << endl;
    cout << setw(15) << setprecision(8) << (X * Y) << endl;

    return 0;
}


// call my_main() - use this to catch exceptions
// use macros for exception names for compatibility with simulated exceptions
int main()
{
    Try{ return my_main(); }
    Catch(BaseException) { cout << BaseException::what() << "\n"; }
    CatchAll{ cout << "\nProgram fails - exception generated\n\n"; }
    return 0;
}

///@}

Chap.II test.cpp (动态库)

半成品没有跑通

/// \ingroup newmat
///@{

/// \file nm_ex1.cpp
/// Very simple example 1.
/// Invert a 4 x 4 matrix then check the result


#define WANT_STREAM       // include iostream and iomanipulators

#include <iostream>
#include <windows.h>

//#include "newmatap.h"     // newmat advanced functions
//                          // should not be required for this example
//                          // included because it seems to help MS VC6
//                          // when you have namespace turned on
//
//#include "newmatio.h"     // newmat headers including output functions


#ifdef use_namespace
using namespace RBD_LIBRARIES;
#endif


int my_main()                  // called by main()
{
    
    HINSTANCE hInst = LoadLibrary(L"LibNewmat_so.dll");   //加载dll库

    typedef void(*Sub)();//函数指针
    Sub PrintHello = (Sub)GetProcAddress(hInst, "PrintHello");//加载库函数
    
    Tracer tr("my_main ");      // for tracking exceptions

    // declare a matrix
    Matrix X(4, 4);

    // load values row by row
    X.row(1) << 3.7 << -2.1 << 7.4 << -1.0;
    X.row(2) << 4.1 << 0.0 << 3.9 << 4.0;
    X.row(3) << -2.5 << 1.9 << -0.4 << 7.3;
    X.row(4) << 1.5 << 9.8 << -2.1 << 1.1;

    // print the matrix
    cout << "Matrix X" << endl;
    cout << setw(15) << setprecision(8) << X << endl;

    // calculate its inverse and print it
    Matrix Y = X.i();
    cout << "Inverse of X" << endl;
    cout << setw(15) << setprecision(8) << Y << endl;

    // multiply X by its inverse and print the result (should be near identity)
    cout << "X * inverse of X" << endl;
    cout << setw(15) << setprecision(8) << (X * Y) << endl;

    FreeLibrary(hInst);             //释放库
    return 0;
}


// call my_main() - use this to catch exceptions
// use macros for exception names for compatibility with simulated exceptions
int main()
{
    Try{ return my_main(); }
    Catch(BaseException) { cout << BaseException::what() << "\n"; }
    CatchAll{ cout << "\nProgram fails - exception generated\n\n"; }
    return 0;

}

///@}

Chap.III 测试文件下载

有关上面的测试文件,笔者进行了整理并上传至 CSDN 资源,感兴趣的朋友可戳我免费下载。文件树如下:

newmat_lib_dll
├─DLL_project			
│  ├─LibNewmat_so		// 生成的动态库 dll
│  └─test_so			// 测试动态库 cpp 文件,半成品
├─LIB_project
│  ├─LibNewmat_a		// 生成的静态库 lib
│  └─test_a				// 测试静态库 cpp 文件
└─newmat_src			// newmat 库源码

Reference

  • 19
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C++中的静态链接和动态链接生成方法略有不同。下面分别介绍: 1. 静态链接(Static Library) 静态链接是指在编译链接阶段将的代码复制到可执行文件中,因此可执行文件不再依赖于文件,可以单独运行。静态链接生成方法如下: - 编写的源代码,生成的目标文件(.o)。 - 将所有目标文件打包成静态链接文件(.a)。 具体的步骤如下: 首先编译的源代码,生成目标文件(.o): ``` g++ -c lib.cpp -o lib.o ``` 然后将目标文件打包成静态链接文件(.a): ``` ar rcs lib.a lib.o ``` 其中,`ar`命令用于打包目标文件,`rcs`参数表示创建文件并插入目标文件,`lib.a`为文件名,`lib.o`为目标文件名。 2. 动态链接(Dynamic Library) 动态链接是指在程序运行时加载文件,因此可执行文件依赖于文件,需要和文件一起运行。动态链接生成方法如下: - 编写的源代码,生成的目标文件(.o)。 - 将所有目标文件编译为共享对象文件(.so)。 具体的步骤如下: 首先编译的源代码,生成目标文件(.o): ``` g++ -c -fPIC lib.cpp -o lib.o ``` 其中,`-fPIC`参数表示生成位置无关代码,是动态链接的必备参数。 然后将目标文件编译为共享对象文件(.so): ``` g++ -shared -o lib.so lib.o ``` 其中,`-shared`参数表示生成共享对象文件,`lib.so`为共享对象文件名,`lib.o`为目标文件名。 以上就是C++生成静态链接和动态链接的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流浪猪头拯救地球

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值