• 从事软件工作也有两年了,C++静态库和动态库也用了不少,但都是依葫芦画瓢,一直没具体去研究一下二者的区别,加载方式等,今天花时间看了几篇博客,重新学习了一下,做出如下笔记。 2、定义 静态库和动态库从字面意思来...

    1、前言

    从事软件工作也有两年了,C++静态库和动态库也用了不少,但都是依葫芦画瓢,一直没具体去研究一下二者的区别,加载方式等,今天花时间看了几篇博客,重新学习了一下,做出如下笔记。

    2、定义

    静态库和动态库从字面意思来看,区别就是静态和动态。而这里的静态和动态指的是库的链接阶段。可以看如下的编译过程。

    • 静态库:在链接阶段库将会与目标汇编后的目标文件.o一起打包生成可执行文件。成为可执行文件的一部分,后续此库就可以消失了。也就是说在编译的最后一步(链接阶段),如果程序需要使用静态库,在这一步都会一起打包到可执行文件中。
    • 动态库:而动态库在编译阶段都不会有什么动作,只有在程序运行时才被加载,也就是动态库的链接是发生在程序运行时期的,它和可执行文件是分开的,只是可执行文件在运行的某个时期调用了它。

    3、优缺点

    分清楚二者区别之后,二者的优缺点就自然可以分出来了。

    优点 缺点
    静态库 1、 使可执行文件依赖项少,已经被打包到可执行文件中了
    2、 编译阶段完成链接,执行期间代码装载速度快
    1、 使可执行文件变大
    2、 若作为其他库的依赖库,将会造成多余的副本,因为必须与目标文件打包
    3、 升级不方便,升级必须重新编译
    动态库 1、 动态库可以实现进程之间资源共享,有一份就行
    2、 升级程序简单,不需要重新编译
    1、 运行期间在加载,将会减慢代码执行速度
    2、 增加程序的依赖项,必须跟着可执行文件一起

    4、文件形式

    Tables Windows Linux
    静态库 .lib Libxxx.a
    动态库 .dll与.lib libxxx.so

    5、生成和加载方式

    这里我仅讨论windows下的使用vs的情况。

    5.1、静态库:

    生成:

    在创建win32控制台程序或者win32项目的时候,勾选静态库就可以,进入项目后也可打开工程“属性面板”—》”配置属性”—》”常规”—》配置类型选择静态库。
    这里写图片描述
    创建好之后,里面在就正常的写函数或者类,将接口放在.h头文件中,编译后边生成了静态库。
    测试代码:

    • 头文件:
      这里写图片描述
    • 源文件:
      这里写图片描述
    • 生成静态库:
      这里写图片描述

    Vs中加载

    需要文件、接口头文件、.lib库文件。

    方式1

    步骤1:包头文件。工程“属性面板”—》”配置属性” —》“C/C++” —》” 常规”,在“附加包含目录”属性值中,键入StaticLib.h 头文件所在目录的路径或浏览至该目录。
    这里写图片描述
    步骤2:“属性面板”—》”配置属性”—》“链接器”—》”常规”,附加依赖库目录中输入,静态库所在目录;
    这里写图片描述
    步骤3:“属性面板”—》”配置属性”—》“链接器”—》”输入”,附加依赖库中输入静态库名StaticLib.lib。
    这里写图片描述
    步骤4:使用,引用头文件,即可使用里面的接口。
    这里写图片描述

    方式2

    步骤1同方式1。
    步骤二:
    打开工程“属性面板”—》”配置属性” —》“链接器”—》”命令行”,输入静态库的完整路径即可。
    这里写图片描述
    方式3:
    步骤1同方式1。
    步骤2:前提,加载的静态库属于同一解决方案。
    工程“属性面板”—》“通用属性”—》 “框架和引用”—》”添加引用”,将显示“添加引用”对话框。 “项目”选项卡列出了当前解决方案中的各个项目以及可以引用的所有库。 在“项目”选项卡中,选择 StaticLib。 单击“确定
    这里写图片描述
    方式4: 在文件中使用#pragma comment()指定.lib文件的位置。如图
    这里写图片描述

    5.2、动态库

    生成

    在创建win32控制台程序或者win32项目的时候,勾选Dll就可以,进入项目后发现自动生成一些文件。如图
    这里写图片描述
    我们还是正常定义和实现函数或者类,特别的是动态库生成的时候需要在接口前加上
    __declspec(dllexport),而导入的时候需要加__declspec(dllimport)。
    这里使用通常的宏定义做法,如图所示。
    头文件:
    这里写图片描述
    源文件:
    这里写图片描述
    生成动态库:
    这里写图片描述
    这里需要注意,生成两个有用文件,一个是.lib,一个是dll。这里的.lib本质上不同于静态库中的.lib。这里的.lib一般是一些索引信息,记录了dll中函数的入口和位置,dll中是函数的具体实现。而静态库中的lib包含了索引和实现。

    加载动态库

    加载动态库有两种方式,分为隐式加载和显示加载。

    隐式加载

    所需文件:接口.h头文件,dll文件,lib文件。
    .h和.lib加载方式与静态加载完全一致。但.dll文件必须放在环境变量指定的目录下。当然通常是与目标.exe文件放在一起。

    显示加载

    所需文件:dll文件。
    利用LoadLibrary()函数进行加载。如图
    这里写图片描述
    按道理不需要.h头文件,但是前提是你知道接口是啥样的。
    隐式加载和显示加载区别很明显,显示加载使用灵活,需要时加载,用完卸载,而隐式加载伴随着整个程序的生命周期。

    6、总结

    库分为静态库和动态库。静态库与可执行文件打包,动态库与可执行文件独立。静态库加载需要.lib和.h文件。动态库隐式加载需要.dll、.h、.lib文件,显示加载只需要.dll文件。

    展开全文
  • 本文只介绍如在visual studio 2008中生成动态库和静态库的方法。。一、生成动态库(Dynamic-link library)的方法第一步:在新建项目中,应用程序设置中,选择DLL,其他不变 然后右击你的项目文件,选择属性,然后...

    本文只介绍如在visual studio 2008中生成动态库和静态库的方法。。

    一、生成动态库(Dynamic-link library)的方法

    第一步:在新建项目中,应用程序设置中,选择DLL,其他不变

    然后右击你的项目文件,选择属性,然后在弹出的属性框中,设置三个地方。

    1.配置属性-连接器-输出文件
    把输出文件名改一下,后缀不要变,例如my.dll
    这里写图片描述

    2.配置属性-C/C++-代码生成
    找到运行时库,改成多线程调试(/MTD)
    这里写图片描述

    3.配置属性-C/C++-预编译头
    找到“创建使用编译头”,设置不使用编译头
    这里写图片描述

    这三步完成后,点击确定
    在项目文件源文件中添加名为mydll.cpp的项目文件,编写代码

    _declspec(dllexport) int sum(int a,int b)
    {
        return a+b;
    }

    然后编译链接执行,右击项目文件,选择在资源管理器中打开,找到生成的my.lib和my.dll这两个文件,只需要这两个文件。

    然后重新打开一个vs2008,正常的建立一个项目,不做任何设置(在生成动态库时,对vs2008设置的一些属性,在新打开的vs中不生效,会回到默认设置,一切正常建立一个项目就可以)

    在新打开的vs中编写代码

    #include<stdio.h>
    #pragma comment(lib,"my.lib")
    _declspec(dllimport) int sum(int a,int b);
    int main()
    {
        int result = sum(3,4);
        printf("result:%d\n",result);
        return 0;
    }

    注意,要把上一个项目中生成的my.dll和my.lib文件拿出来,放在本项目中CPP文件一个目录下。
    然后编译链接执行。

    二、生成静态库的方法
    在创建项目时,只需要以下设置就可以了。
    应用程序设置中勾选静态库,取消使用预编译头的勾选
    这里写图片描述

    然后把输出文件改为my.lib
    编写代码

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

    编译链接生成后再资源管理器中找到生成的my.lib文件。
    此时打开一个新的vs,正常的创建一个项目
    编写以下代码

    #include<stdio.h>
    #pragma comment(lib,"my.lib")
    int sum(int a,int b);
    int main()
    {
        int ret = sum(2,3);
        printf("ret:%d\n",ret);
        return 0;
    }
    展开全文
  • C++静态库动态库 这次分享的宗旨是——让大家学会创建与使用静态库动态库,知道静态库动态库的区别,知道使用的时候如何选择。这里不深入介绍静态库动态库的底层格式,内存布局等,有兴趣的同学,推荐一本...

                                                                                        C++静态库与动态库

    这次分享的宗旨是——让大家学会创建与使用静态库、动态库,知道静态库与动态库的区别,知道使用的时候如何选择。这里不深入介绍静态库、动态库的底层格式,内存布局等,有兴趣的同学,推荐一本书《程序员的自我修养——链接、装载与库》。

    什么是库

    库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常

    本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:静态库(.a、.lib)和动态库(.so、.dll)。

    所谓静态、动态是指链接。回顾一下,将一个程序编译成可执行程序的步骤:

      clip_image002[4]

    图:编译过程

    静态库

    之所以成为【静态库】,是因为在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。

    试想一下,静态库与汇编生成的目标文件一起链接为可执行文件,那么静态库必定跟.o文件格式相似。其实一个静态库可以简单看成是一组目标文件(.o/.obj文件)的集合,即很多目标文件经过压缩打包后形成的一个文件。静态库特点总结:

    l  静态库对函数库的链接是放在编译时期完成的。

    l  程序在运行时与函数库再无瓜葛,移植方便。

    l  浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。

    下面编写一些简单的四则运算C++类,将其编译成静态库给他人用,头文件如下所示:

    StaticMath.h头文件

    #pragma once

    class StaticMath

    {

    public:

        StaticMath(void);

        ~StaticMath(void);

     

        static double add(double a, double b);//加法

        static double sub(double a, double b);//减法

        static double mul(double a, double b);//乘法

        static double div(double a, double b);//除法

     

        void print();

    };

     

    Linux下使用ar工具、Windows下vs使用lib.exe,将目标文件压缩到一起,并且对其进行编号和索引,以便于查找和检索。一般创建静态库的步骤如图所示:

                 clip_image004[4]

    图:创建静态库过程

    Linux下创建与使用静态库

    Linux静态库命名规则

    Linux静态库命名规范,必须是"lib[your_library_name].a":lib为前缀,中间是静态库名,扩展名为.a。

    创建静态库(.a)

    通过上面的流程可以知道,Linux创建静态库过程如下:

    l  首先,将代码文件编译成目标文件.o(StaticMath.o)

    g++ -c StaticMath.cpp

    注意带参数-c,否则直接编译为可执行文件

    l  然后,通过ar工具将目标文件打包成.a静态库文件

    ar -crv libstaticmath.a StaticMath.o

    生成静态库libstaticmath.a

                                     clip_image006[4]

    大一点的项目会编写makefile文件(CMake等等工程管理工具)来生成静态库,输入多个命令太麻烦了。

    使用静态库

    编写使用上面创建的静态库的测试代码:

    测试代码:

    #include "StaticMath.h"

    #include <iostream>

    using namespace std;

     

    int main(int argc, char* argv[])

    {

        double a = 10;

        double b = 2;

     

        cout << "a + b = " << StaticMath::add(a, b) << endl;

        cout << "a - b = " << StaticMath::sub(a, b) << endl;

        cout << "a * b = " << StaticMath::mul(a, b) << endl;

        cout << "a / b = " << StaticMath::div(a, b) << endl;

     

        StaticMath sm;

        sm.print();

     

        system("pause");

        return 0;

    }

    Linux下使用静态库,只需要在编译的时候,指定静态库的搜索路径(-L选项)、指定静态库名(不需要lib前缀和.a后缀,-l选项)。

    # g++ TestStaticLibrary.cpp -L../StaticLibrary -lstaticmath

                                    clip_image008[4]

    l  -L:表示要连接的库所在目录

    l  -l:指定链接时需要的动态库,编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.a或.so来确定库的名称。

     

    Windows下创建与使用静态库

    创建静态库(.lib)

    如果是使用VS命令行生成静态库,也是分两个步骤来生成程序:

    l  首先,通过使用带编译器选项 /c 的 Cl.exe 编译代码 (cl /c StaticMath.cpp),创建名为“StaticMath.obj”的目标文件。

    l  然后,使用库管理器 Lib.exe 链接代码 (lib StaticMath.obj),创建静态库StaticMath.lib。

    当然,我们一般不这么用,使用VS工程设置更方便。创建win32控制台程序时,勾选静态库类型;打开工程“属性面板”è”配置属性”è”常规”,配置类型选择静态库。

                                   clip_image010[4]

    图:vs静态库项目属性设置

    Build项目即可生成静态库。

    使用静态库

    测试代码Linux下面的一样。有3种使用方法:

    方法一:

    在VS中使用静态库方法:

    l  工程“属性面板”è“通用属性”è “框架和引用”è”添加引用”,将显示“添加引用”对话框。 “项目”选项卡列出了当前解决方案中的各个项目以及可以引用的所有库。 在“项目”选项卡中,选择 StaticLibrary。 单击“确定”。

                                        clip_image012[4]

    l  添加StaticMath.h 头文件目录,必须修改包含目录路径。打开工程“属性面板”è”配置属性”è “C/C++”è” 常规”,在“附加包含目录”属性值中,键入StaticMath.h 头文件所在目录的路径或浏览至该目录。

                                           clip_image014[4]

    编译运行OK。

                                                             clip_image015[4]

    图:静态库测试结果(vs)

    如果引用的静态库不是在同一解决方案下的子工程,而是使用第三方提供的静态库lib和头文件,上面的方法设置不了。还有2中方法设置都可行。

    方法二:

    打开工程“属性面板”è”配置属性”è “链接器”è”命令行”,输入静态库的完整路径即可。

                                    clip_image017[4]

    方法三:

    l  “属性面板”è”配置属性”è “链接器”è”常规”,附加依赖库目录中输入,静态库所在目录;

    l  “属性面板”è”配置属性”è “链接器”è”输入”,附加依赖库中输入静态库名StaticLibrary.lib。

                                      clip_image019[4]

    动态库

    通过上面的介绍发现静态库,容易使用和理解,也达到了代码复用的目的,那为什么还需要动态库呢?

    为什么还需要动态库?

    为什么需要动态库,其实也是静态库的特点导致。

    l  空间浪费是静态库的一个问题。

                                           clip_image021[4]

    l  另一个问题是静态库对程序的更新、部署和发布页会带来麻烦。如果静态库liba.lib更新了,所以使用它的应用程序都需要重新编译、发布给用户(对于玩家来说,可能是一个很小的改动,却导致整个程序重新下载,全量更新)。

    动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新

                                              clip_image023[4]

    动态库特点总结:

    l  动态库把对一些库函数的链接载入推迟到程序运行的时期。

    l  可以实现进程之间的资源共享。(因此动态库也称为共享库)

    l  将一些程序升级变得简单。

    l  甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)。

    Window与Linux执行文件格式不同,在创建动态库的时候有一些差异。

    l  在Windows系统下的执行文件格式是PE格式,动态库需要一个DllMain函数做出初始化的入口,通常在导出函数的声明时需要有_declspec(dllexport)关键字

    l  Linux下gcc编译的执行文件默认是ELF格式,不需要初始化入口,亦不需要函数做特别的声明,编写比较方便。

    与创建静态库不同的是,不需要打包工具(ar、lib.exe),直接使用编译器即可创建动态库。

    Linux下创建与使用动态库

     

    linux动态库的命名规则

    动态链接库的名字形式为 libxxx.so,前缀是lib,后缀名为“.so”。

    l  针对于实际库文件,每个共享库都有个特殊的名字“soname”。在程序启动后,程序通过这个名字来告诉动态加载器该载入哪个共享库。

    l  在文件系统中,soname仅是一个链接到实际动态库的链接。对于动态库而言,每个库实际上都有另一个名字给编译器来用。它是一个指向实际库镜像文件的链接文件(lib+soname+.so)。

    创建动态库(.so)

    编写四则运算动态库代码:

    DynamicMath.h头文件

    #pragma once

    class DynamicMath

    {

    public:

            DynamicMath(void);

            ~DynamicMath(void);

     

            static double add(double a, double b);//¼Ó·¨

            static double sub(double a, double b);//¼õ·¨

            static double mul(double a, double b);//³Ë·¨

            static double div(double a, double b);//³ý·¨

            void print();

    };

     

    l  首先,生成目标文件,此时要加编译器选项-fpic

    g++ -fPIC -c DynamicMath.cpp

    -fPIC 创建与地址无关的编译程序(pic,position independent code),是为了能够在多个应用程序间共享。

    l  然后,生成动态库,此时要加链接器选项-shared

    g++ -shared -o libdynmath.so DynamicMath.o

    -shared指定生成动态链接库。

                                  clip_image025[4]

    其实上面两个步骤可以合并为一个命令:

    g++ -fPIC -shared -o libdynmath.so DynamicMath.cpp

     

    使用动态库

    编写使用动态库的测试代码:

    测试代码:

    #include "../DynamicLibrary/DynamicMath.h"

     

    #include <iostream>

    using namespace std;

     

    int main(int argc, char* argv[])

    {

        double a = 10;

        double b = 2;

     

        cout << "a + b = " << DynamicMath::add(a, b) << endl;

        cout << "a - b = " << DynamicMath::sub(a, b) << endl;

        cout << "a * b = " << DynamicMath::mul(a, b) << endl;

        cout << "a / b = " << DynamicMath::div(a, b) << endl;

     

        DynamicMath dyn;

        dyn.print();

        return 0;

    }

    引用动态库编译成可执行文件(跟静态库方式一样):

    g++ TestDynamicLibrary.cpp -L../DynamicLibrary -ldynmath

    然后运行:./a.out,发现竟然报错了!!!

                                    clip_image027[4]

    可能大家会猜测,是因为动态库跟测试程序不是一个目录,那我们验证下是否如此:

                                    clip_image029[4]

    发现还是报错!!!那么,在执行的时候是如何定位共享库文件的呢?

    1)        当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径。此时就需要系统动态载入器(dynamic linker/loader)。

    2)        对于elf格式的可执行程序,是由ld-linux.so*来完成的,它先后搜索elf文件的DT_RPATH段—环境变量LD_LIBRARY_PATH—/etc/ld.so.cache文件列表—/lib/,/usr/lib 目录找到库文件后将其载入内存。

    如何让系统能够找到它:

    l  如果安装在/lib或者/usr/lib下,那么ld默认能够找到,无需其他操作。

    l  如果安装在其他目录,需要将其添加到/etc/ld.so.cache文件中,步骤如下:

    n  编辑/etc/ld.so.conf文件,加入库文件所在目录的路径

    n  运行ldconfig ,该命令会重建/etc/ld.so.cache文件

    我们将创建的动态库复制到/usr/lib下面,然后运行测试程序。

                                    clip_image031[4]

    Windows下创建与使用动态库

    创建动态库(.dll)

    与Linux相比,在Windows系统下创建动态库要稍微麻烦一些。首先,需要一个DllMain函数做出初始化的入口(创建win32控制台程序时,勾选DLL类型会自动生成这个文件):

    dllmain.cpp入口文件

    // dllmain.cpp : Defines the entry point for the DLL application.

    #include "stdafx.h"

     

    BOOL APIENTRY DllMain( HMODULE hModule,

                           DWORD  ul_reason_for_call,

                           LPVOID lpReserved

                         )

    {

        switch (ul_reason_for_call)

        {

        case DLL_PROCESS_ATTACH:

        case DLL_THREAD_ATTACH:

        case DLL_THREAD_DETACH:

        case DLL_PROCESS_DETACH:

            break;

        }

        return TRUE;

    }

    通常在导出函数的声明时需要有_declspec(dllexport)关键字:

    DynamicMath.h头文件

    #pragma once

    class DynamicMath

    {

    public:

        __declspec(dllexport) DynamicMath(void);

        __declspec(dllexport) ~DynamicMath(void);

     

        static __declspec(dllexport) double add(double a, double b);//加法

        static __declspec(dllexport) double sub(double a, double b);//减法

        static __declspec(dllexport) double mul(double a, double b);//乘法

        static __declspec(dllexport) double div(double a, double b);//除法

     

        __declspec(dllexport) void print();

    };

    生成动态库需要设置工程属性,打开工程“属性面板”è”配置属性”è”常规”,配置类型选择动态库。

                                         clip_image033[4]

    图:v动态库项目属性设置

    Build项目即可生成动态库。

    使用动态库

    创建win32控制台测试程序:

    TestDynamicLibrary.cpp测试程序

    #include "stdafx.h"

    #include "DynamicMath.h"

     

    #include <iostream>

    using namespace std;

     

    int _tmain(int argc, _TCHAR* argv[])

    {

        double a = 10;

        double b = 2;

     

        cout << "a + b = " << DynamicMath::add(a, b) << endl;

        cout << "a - b = " << DynamicMath::sub(a, b) << endl;

        cout << "a * b = " << DynamicMath::mul(a, b) << endl;

        cout << "a / b = " << DynamicMath::div(a, b) << endl;

     

        DynamicMath dyn;

        dyn.print();

     

        system("pause");

        return 0;

    }

    方法一:

    l  工程“属性面板”è“通用属性”è “框架和引用”è”添加引用”,将显示“添加引用”对话框。“项目”选项卡列出了当前解决方案中的各个项目以及可以引用的所有库。 在“项目”选项卡中,选择 DynamicLibrary。 单击“确定”。

                                       clip_image035[4]

    l  添加DynamicMath.h 头文件目录,必须修改包含目录路径。打开工程“属性面板”è”配置属性”è “C/C++”è” 常规”,在“附加包含目录”属性值中,键入DynamicMath.h 头文件所在目录的路径或浏览至该目录。

                                           clip_image037[4]

    编译运行OK。

                                                                 clip_image038[4]

    图:动态库测试结果(vs)

    方法二:

    l  “属性面板”è”配置属性”è “链接器”è”常规”,附加依赖库目录中输入,动态库所在目录;

                                      clip_image040[4]

    l  “属性面板”è”配置属性”è “链接器”è”输入”,附加依赖库中输入动态库编译出来的DynamicLibrary.lib。

                                      clip_image042[4]

    这里可能大家有个疑问,动态库怎么还有一个DynamicLibrary.lib文件?即无论是静态链接库还是动态链接库,最后都有lib文件,那么两者区别是什么呢?其实,两个是完全不一样的东西。

                                           clip_image044[4]

    StaticLibrary.lib的大小为190KB,DynamicLibrary.lib的大小为3KB,静态库对应的lib文件叫静态库,动态库对应的lib文件叫【导入库】。实际上静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息

    动态库的显式调用

    上面介绍的动态库使用方法和静态库类似属于隐式调用,编译的时候指定相应的库和查找路径。其实,动态库还可以显式调用。【在C语言中】,显示调用一个动态库轻而易举!

    在Linux下显式调用动态库

    #include <dlfcn.h>,提供了下面几个接口:

    l  void * dlopen( const char * pathname, int mode ):函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。

    l  void* dlsym(void* handle,const char* symbol):dlsym根据动态链接库操作句柄(pHandle)与符号(symbol),返回符号对应的地址。使用这个函数不但可以获取函数地址,也可以获取变量地址。

    l  int dlclose (void *handle):dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。

    l  const char *dlerror(void):当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。

    在Windows下显式调用动态库

    应用程序必须进行函数调用以在运行时显式加载 DLL。为显式链接到 DLL,应用程序必须:

    l  调用 LoadLibrary(或相似的函数)以加载 DLL 和获取模块句柄。

    l  调用 GetProcAddress,以获取指向应用程序要调用的每个导出函数的函数指针。由于应用程序是通过指针调用 DLL 的函数,编译器不生成外部引用,故无需与导入库链接。

    l  使用完 DLL 后调用 FreeLibrary

    显式调用C++动态库注意点

    对C++来说,情况稍微复杂。显式加载一个C++动态库的困难一部分是因为C++的name mangling另一部分是因为没有提供一个合适的API来装载类,在C++中,您可能要用到库中的一个类,而这需要创建该类的一个实例,这不容易做到。

    name mangling可以通过extern "C"解决。C++有个特定的关键字用来声明采用C binding的函数:extern "C" 。用 extern "C"声明的函数将使用函数名作符号名,就像C函数一样。因此,只有非成员函数才能被声明为extern "C",并且不能被重载。尽管限制多多,extern "C"函数还是非常有用,因为它们可以象C函数一样被dlopen动态加载。冠以extern "C"限定符后,并不意味着函数中无法使用C++代码了,相反,它仍然是一个完全的C++函数,可以使用任何C++特性和各种类型的参数。

    另外如何从C++动态库中获取类,附上几篇相关文章,但我并不建议这么做:

    l  《LoadLibrary调用DLL中的Class》:http://www.cppblog.com/codejie/archive/2009/09/24/97141.html

    l  《C++ dlopen mini HOWTO》:http://blog.csdn.net/denny_233/article/details/7255673

    “显式”使用C++动态库中的Class是非常繁琐和危险的事情,因此能用“隐式”就不要用“显式”,能静态就不要用动态。

    附件:Linux下库相关命令

    g++(gcc)编译选项

    l  -shared :指定生成动态链接库。

    l  -static :指定生成静态链接库。

    l  -fPIC :表示编译为位置独立的代码,用于编译共享库。目标文件需要创建成位置无关码, 念上就是在可执行程序装载它们的时候,它们可以放在可执行程序的内存里的任何地方。

    l  -L. :表示要连接的库所在的目录。

    l  -l:指定链接时需要的动态库。编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.a/.so来确定库的名称。

    l  -Wall :生成所有警告信息。

    l  -ggdb :此选项将尽可能的生成gdb 的可以使用的调试信息。

    l  -g :编译器在编译的时候产生调试信息。

    l  -c :只激活预处理、编译和汇编,也就是把程序做成目标文件(.o文件) 。

    l  -Wl,options :把参数(options)传递给链接器ld 。如果options 中间有逗号,就将options分成多个选项,然后传递给链接程序。

    nm命令

    有时候可能需要查看一个库中到底有哪些函数,nm命令可以打印出库中的涉及到的所有符号。库既可以是静态的也可以是动态的。nm列出的符号有很多,常见的有三种:

    l  一种是在库中被调用,但并没有在库中定义(表明需要其他库支持),用U表示;

    l  一种是库中定义的函数,用T表示,这是最常见的;

    l  一种是所谓的弱态”符号,它们虽然在库中被定义,但是可能被其他库中的同名符号覆盖,用W表示。

    $nm libhello.h

    ldd命令

    ldd命令可以查看一个可执行程序依赖的共享库,例如我们编写的四则运算动态库依赖下面这些库:

                              clip_image046[4]

    总结

    二者的不同点在于代码被载入的时刻不同

    l  静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库,因此体积较大

    l  动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在,因此代码体积较小

    动态库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。带来好处的同时,也会有问题!如经典的DLL Hell问题,关于如何规避动态库管理问题,可以自行查找相关资料。

    作者:吴秦
    出处:http://www.cnblogs.com/skynet/
    本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名吴秦(包含链接).

     

    展开全文
  • 这次分享的宗旨是——让大家学会创建与使用静态库动态库,知道静态库动态库的区别,知道使用的时候如何选择。这里不深入介绍静态库动态库的底层格式,内存布局等,有兴趣的同学,推荐一本书《程序员的自我修养...

    这次分享的宗旨是——让大家学会创建与使用静态库、动态库,知道静态库与动态库的区别,知道使用的时候如何选择。这里不深入介绍静态库、动态库的底层格式,内存布局等,有兴趣的同学,推荐一本书《程序员的自我修养——链接、装载与库》。

    什么是库

    库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常

    本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:静态库(.a.lib)和动态库(.so.dll)。

    所谓静态、动态是指链接。回顾一下,将一个程序编译成可执行程序的步骤:

    clip_image002[4]

    图:编译过程

    静态库

    之所以成为【静态库】,是因为在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。

    试想一下,静态库与汇编生成的目标文件一起链接为可执行文件,那么静态库必定跟.o文件格式相似。其实一个静态库可以简单看成是一组目标文件(.o/.obj文件)的集合,即很多目标文件经过压缩打包后形成的一个文件。静态库特点总结:

    l  静态库对函数库的链接是放在编译时期完成的。

    l  程序在运行时与函数库再无瓜葛,移植方便。

    l  浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。

    下面编写一些简单的四则运算C++类,将其编译成静态库给他人用,头文件如下所示:

    StaticMath.h头文件

    #pragma once

    class StaticMath

    {

    public:

        StaticMath(void);

        ~StaticMath(void);

     

        static double add(double a, double b);//加法

        static double sub(double a, double b);//减法

        static double mul(double a, double b);//乘法

        static double div(double a, double b);//除法

     

        void print();

    };

     

    Linux下使用ar工具、Windowsvs使用lib.exe,将目标文件压缩到一起,并且对其进行编号和索引,以便于查找和检索。一般创建静态库的步骤如图所示:

    clip_image004[4]

    图:创建静态库过程

    Linux下创建与使用静态库

    Linux静态库命名规则

    Linux静态库命名规范,必须是"lib[your_library_name].a"lib为前缀,中间是静态库名,扩展名为.a

    创建静态库(.a

    通过上面的流程可以知道,Linux创建静态库过程如下:

    l  首先,将代码文件编译成目标文件.oStaticMath.o

    g++ -c StaticMath.cpp

    注意带参数-c,否则直接编译为可执行文件

    l  然后,通过ar工具将目标文件打包成.a静态库文件

    ar -crv libstaticmath.a StaticMath.o

    生成静态库libstaticmath.a

    clip_image006[4]

    大一点的项目会编写makefile文件(CMake等等工程管理工具)来生成静态库,输入多个命令太麻烦了。

    使用静态库

    编写使用上面创建的静态库的测试代码:

    测试代码:

    #include "StaticMath.h"

    #include <iostream>

    using namespace std;

     

    int main(int argccharargv[])

    {

        double a = 10;

        double b = 2;

     

        cout << "a + b = " << StaticMath::add(a, b) << endl;

        cout << "a - b = " << StaticMath::sub(a, b) << endl;

        cout << "a * b = " << StaticMath::mul(a, b) << endl;

        cout << "a / b = " << StaticMath::div(a, b) << endl;

     

        StaticMath sm;

        sm.print();

     

        system("pause");

        return 0;

    }

    Linux下使用静态库,只需要在编译的时候,指定静态库的搜索路径(-L选项)、指定静态库名(不需要lib前缀和.a后缀,-l选项)。

    # g++ TestStaticLibrary.cpp -L../StaticLibrary -lstaticmath

    clip_image008[4]

    l  -L:表示要连接的库所在目录

    l  -l:指定链接时需要的动态库,编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.a.so来确定库的名称。

     

    Windows下创建与使用静态库

    创建静态库(.lib

    如果是使用VS命令行生成静态库,也是分两个步骤来生成程序:

    l  首先,通过使用带编译器选项 /c  Cl.exe 编译代码 (cl /c StaticMath.cpp),创建名为“StaticMath.obj”的目标文件。

    l  然后,使用库管理器 Lib.exe 链接代码 (lib StaticMath.obj),创建静态库StaticMath.lib

    当然,我们一般不这么用,使用VS工程设置更方便。创建win32控制台程序时,勾选静态库类型;打开工程属性面板è配置属性è常规,配置类型选择静态库。

    clip_image010[4]

    图:vs静态库项目属性设置

    Build项目即可生成静态库。

    使用静态库

    测试代码Linux下面的一样。有3种使用方法:

    方法一:

    VS中使用静态库方法:

    l  工程属性面板è通用属性è “框架和引用è添加引用,将显示添加引用对话框。 “项目选项卡列出了当前解决方案中的各个项目以及可以引用的所有库。 项目选项卡中,选择 StaticLibrary单击确定

    clip_image012[4]

    l  添加StaticMath.h 头文件目录,必须修改包含目录路径。打开工程属性面板è配置属性è“C/C++”è” 常规,在附加包含目录属性值中,键入StaticMath.h 头文件所在目录的路径或浏览至该目录。

    clip_image014[4]

    编译运行OK

    clip_image015[4]

    图:静态库测试结果(vs

    如果引用的静态库不是在同一解决方案下的子工程,而是使用第三方提供的静态库lib和头文件,上面的方法设置不了。还有2中方法设置都可行。

    方法二:

    打开工程属性面板è配置属性è “链接器è命令行,输入静态库的完整路径即可。

    clip_image017[4]

    方法三:

    l  属性面板è配置属性è “链接器è常规,附加依赖库目录中输入,静态库所在目录;

    l  属性面板è配置属性è “链接器è输入,附加依赖库中输入静态库名StaticLibrary.lib

    clip_image019[4]

    动态库

    通过上面的介绍发现静态库,容易使用和理解,也达到了代码复用的目的,那为什么还需要动态库呢?

    为什么还需要动态库?

    为什么需要动态库,其实也是静态库的特点导致。

    l  空间浪费是静态库的一个问题。

    clip_image021[4]

    l  另一个问题是静态库对程序的更新、部署和发布页会带来麻烦。如果静态库liba.lib更新了,所以使用它的应用程序都需要重新编译、发布给用户(对于玩家来说,可能是一个很小的改动,却导致整个程序重新下载,全量更新)。

    动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新

    clip_image023[4]

    动态库特点总结:

    l  动态库把对一些库函数的链接载入推迟到程序运行的时期。

    l  可以实现进程之间的资源共享。(因此动态库也称为共享库)

    l  将一些程序升级变得简单。

    l  甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)。

    WindowLinux执行文件格式不同,在创建动态库的时候有一些差异。

    l  Windows系统下的执行文件格式是PE格式,动态库需要一个DllMain函数做出初始化的入口,通常在导出函数的声明时需要有_declspec(dllexport)关键字

    l  Linuxgcc编译的执行文件默认是ELF格式,不需要初始化入口,亦不需要函数做特别的声明,编写比较方便。

    与创建静态库不同的是,不需要打包工具(arlib.exe),直接使用编译器即可创建动态库。

    Linux下创建与使用动态库

     

    linux动态库的命名规则

    动态链接库的名字形式为 libxxx.so,前缀是lib,后缀名为“.so”。

    l  针对于实际库文件,每个共享库都有个特殊的名字“soname”。在程序启动后,程序通过这个名字来告诉动态加载器该载入哪个共享库。

    l  在文件系统中,soname仅是一个链接到实际动态库的链接。对于动态库而言,每个库实际上都有另一个名字给编译器来用。它是一个指向实际库镜像文件的链接文件(lib+soname+.so)。

    创建动态库(.so

    编写四则运算动态库代码:

    DynamicMath.h头文件

    #pragma once

    class DynamicMath

    {

    public:

            DynamicMath(void);

            ~DynamicMath(void);

     

            static double add(double a, double b);//¼Ó·¨

            static double sub(double a, double b);//¼õ·¨

            static double mul(double a, double b);//³Ë·¨

            static double div(double a, double b);//³ý·¨

            void print();

    };

     

    l  首先,生成目标文件,此时要加编译器选项-fpic

    g++ -fPIC -c DynamicMath.cpp

    -fPIC 创建与地址无关的编译程序(picposition independent code),是为了能够在多个应用程序间共享。

    l  然后,生成动态库,此时要加链接器选项-shared

    g++ -shared -o libdynmath.so DynamicMath.o

    -shared指定生成动态链接库。

    clip_image025[4]

    其实上面两个步骤可以合并为一个命令:

    g++ -fPIC -shared -o libdynmath.so DynamicMath.cpp

     

    使用动态库

    编写使用动态库的测试代码:

    测试代码:

    #include "../DynamicLibrary/DynamicMath.h"

     

    #include <iostream>

    using namespace std;

     

    int main(int argc, char* argv[])

    {

        double a = 10;

        double b = 2;

     

        cout << "a + b = " << DynamicMath::add(a, b) << endl;

        cout << "a - b = " << DynamicMath::sub(a, b) << endl;

        cout << "a * b = " << DynamicMath::mul(a, b) << endl;

        cout << "a / b = " << DynamicMath::div(a, b) << endl;

     

        DynamicMath dyn;

        dyn.print();

        return 0;

    }

    引用动态库编译成可执行文件(跟静态库方式一样):

    g++ TestDynamicLibrary.cpp -L../DynamicLibrary -ldynmath

    然后运行:./a.out,发现竟然报错了!!!

    clip_image027[4]

    可能大家会猜测,是因为动态库跟测试程序不是一个目录,那我们验证下是否如此:

    clip_image029[4]

    发现还是报错!!!那么,在执行的时候是如何定位共享库文件的呢?

    1)        当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径。此时就需要系统动态载入器(dynamic linker/loader)

    2)        对于elf格式的可执行程序,是由ld-linux.so*来完成的,它先后搜索elf文件的 DT_RPATH段—环境变量LD_LIBRARY_PATH/etc/ld.so.cache文件列表—/lib/,/usr/lib 目录找到库文件后将其载入内存。

    如何让系统能够找到它:

    l  如果安装在/lib或者/usr/lib下,那么ld默认能够找到,无需其他操作。

    l  如果安装在其他目录,需要将其添加到/etc/ld.so.cache文件中,步骤如下:

    n  编辑/etc/ld.so.conf文件,加入库文件所在目录的路径

    n  运行ldconfig ,该命令会重建/etc/ld.so.cache文件

    我们将创建的动态库复制到/usr/lib下面,然后运行测试程序。

    clip_image031[4]

    Windows下创建与使用动态库

    创建动态库(.dll

    Linux相比,在Windows系统下创建动态库要稍微麻烦一些。首先,需要一个DllMain函数做出初始化的入口(创建win32控制台程序时,勾选DLL类型会自动生成这个文件):

    dllmain.cpp入口文件

    // dllmain.cpp : Defines the entry point for the DLL application.

    #include "stdafx.h"

     

    BOOL APIENTRY DllMain( HMODULE hModule,

                           DWORD  ul_reason_for_call,

                           LPVOID lpReserved

                         )

    {

        switch (ul_reason_for_call)

        {

        case DLL_PROCESS_ATTACH:

        case DLL_THREAD_ATTACH:

        case DLL_THREAD_DETACH:

        case DLL_PROCESS_DETACH:

            break;

        }

        return TRUE;

    }

    通常在导出函数的声明时需要有_declspec(dllexport)关键字:

    DynamicMath.h头文件

    #pragma once

    class DynamicMath

    {

    public:

        __declspec(dllexport) DynamicMath(void);

        __declspec(dllexport) ~DynamicMath(void);

     

        static __declspec(dllexportdouble add(double a, double b);//加法

        static __declspec(dllexportdouble sub(double a, double b);//减法

        static __declspec(dllexportdouble mul(double a, double b);//乘法

        static __declspec(dllexportdouble div(double a, double b);//除法

     

        __declspec(dllexportvoid print();

    };

    生成动态库需要设置工程属性,打开工程属性面板è配置属性è常规,配置类型选择动态库。

    clip_image033[4]

    图:v动态库项目属性设置

    Build项目即可生成动态库。

    使用动态库

    创建win32控制台测试程序:

    TestDynamicLibrary.cpp测试程序

    #include "stdafx.h"

    #include "DynamicMath.h"

     

    #include <iostream>

    using namespace std;

     

    int _tmain(int argc_TCHARargv[])

    {

        double a = 10;

        double b = 2;

     

        cout << "a + b = " << DynamicMath::add(a, b) << endl;

        cout << "a - b = " << DynamicMath::sub(a, b) << endl;

        cout << "a * b = " << DynamicMath::mul(a, b) << endl;

        cout << "a / b = " << DynamicMath::div(a, b) << endl;

     

        DynamicMath dyn;

        dyn.print();

     

        system("pause");

        return 0;

    }

    方法一:

    l  工程属性面板è通用属性è “框架和引用è添加引用,将显示添加引用对话框。项目选项卡列出了当前解决方案中的各个项目以及可以引用的所有库。 项目选项卡中,选择DynamicLibrary 单击确定

    clip_image035[4]

    l  添加DynamicMath.h 头文件目录,必须修改包含目录路径。打开工程属性面板è配置属性è“C/C++”è” 常规,在附加包含目录属性值中,键入DynamicMath.h 头文件所在目录的路径或浏览至该目录。

    clip_image037[4]

    编译运行OK

    clip_image038[4]

    图:动态库测试结果(vs

    方法二:

    l  属性面板è配置属性è “链接器è常规,附加依赖库目录中输入,动态库所在目录;

    clip_image040[4]

    l  属性面板è配置属性è “链接器è输入,附加依赖库中输入动态库编译出来的DynamicLibrary.lib

    clip_image042[4]

    这里可能大家有个疑问,动态库怎么还有一个DynamicLibrary.lib文件?即无论是静态链接库还是动态链接库,最后都有lib文件,那么两者区别是什么呢?其实,两个是完全不一样的东西。

    clip_image044[4]

    StaticLibrary.lib的大小为190KBDynamicLibrary.lib的大小为3KB,静态库对应的lib文件叫静态库,动态库对应的lib文件叫【导入库】。实际上静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息

    动态库的显式调用

    上面介绍的动态库使用方法和静态库类似属于隐式调用,编译的时候指定相应的库和查找路径。其实,动态库还可以显式调用。【在C语言中】,显示调用一个动态库轻而易举!

    Linux下显式调用动态库

    #include <dlfcn.h>,提供了下面几个接口:

    l  void * dlopen( const char * pathname, int mode ):函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。

    l  void* dlsym(void* handle,const char* symbol)dlsym根据动态链接库操作句柄(pHandle)与符号(symbol),返回符号对应的地址。使用这个函数不但可以获取函数地址,也可以获取变量地址。

    l  int dlclose (void *handle)dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0,才会真正被系统卸载。

    l  const char *dlerror(void):当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。

    Windows下显式调用动态库

    应用程序必须进行函数调用以在运行时显式加载 DLL。为显式链接到 DLL,应用程序必须:

    l  调用 LoadLibrary(或相似的函数)以加载 DLL 和获取模块句柄。

    l  调用 GetProcAddress,以获取指向应用程序要调用的每个导出函数的函数指针。由于应用程序是通过指针调用 DLL 的函数,编译器不生成外部引用,故无需与导入库链接。

    l  使用完 DLL 后调用 FreeLibrary

    显式调用C++动态库注意点

    C++来说,情况稍微复杂。显式加载一个C++动态库的困难一部分是因为C++name mangling另一部分是因为没有提供一个合适的API来装载类,在C++中,您可能要用到库中的一个类,而这需要创建该类的一个实例,这不容易做到。

    name mangling可以通过extern "C"解决。C++有个特定的关键字用来声明采用C binding的函数:extern "C" 。用 extern "C"声明的函数将使用函数名作符号名,就像C函数一样。因此,只有非成员函数才能被声明为extern "C",并且不能被重载。尽管限制多多,extern "C"函数还是非常有用,因为它们可以象C函数一样被dlopen动态加载。冠以extern "C"限定符后,并不意味着函数中无法使用C++代码了,相反,它仍然是一个完全的C++函数,可以使用任何C++特性和各种类型的参数。

    另外如何从C++动态库中获取类,附上几篇相关文章,但我并不建议这么做:

    l  LoadLibrary调用DLL中的Class》:http://www.cppblog.com/codejie/archive/2009/09/24/97141.html

    l  C++ dlopen mini HOWTO》:http://blog.csdn.net/denny_233/article/details/7255673

    “显式”使用C++动态库中的Class是非常繁琐和危险的事情,因此能用“隐式”就不要用“显式”,能静态就不要用动态。

    附件:Linux下库相关命令

    g++(gcc)编译选项

    l  -shared :指定生成动态链接库。

    l  -static :指定生成静态链接库。

    l  -fPIC :表示编译为位置独立的代码,用于编译共享库。目标文件需要创建成位置无关码, 念上就是在可执行程序装载它们的时候,它们可以放在可执行程序的内存里的任何地方。

    l  -L. :表示要连接的库所在的目录。

    l  -l:指定链接时需要的动态库。编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.a/.so来确定库的名称。

    l  -Wall :生成所有警告信息。

    l  -ggdb :此选项将尽可能的生成gdb 的可以使用的调试信息。

    l  -g :编译器在编译的时候产生调试信息。

    l  -c :只激活预处理、编译和汇编,也就是把程序做成目标文件(.o文件

    l  -Wl,options :把参数(options)传递给链接器ld 。如果options 中间有逗号,就将options分成多个选项,然后传递给链接程序。

    nm命令

    有时候可能需要查看一个库中到底有哪些函数,nm命令可以打印出库中的涉及到的所有符号。库既可以是静态的也可以是动态的。nm列出的符号有很多,常见的有三种:

    l  一种是在库中被调用,但并没有在库中定义(表明需要其他库支持),用U表示;

    l  一种是库中定义的函数,用T表示,这是最常见的;

    l  一种是所谓的弱态”符号,它们虽然在库中被定义,但是可能被其他库中的同名符号覆盖,用W表示。

    $nm libhello.h

    ldd命令

    ldd命令可以查看一个可执行程序依赖的共享库,例如我们编写的四则运算动态库依赖下面这些库:

    clip_image046[4]

    总结

    二者的不同点在于代码被载入的时刻不同

    l  静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库,因此体积较大

    l  动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在,因此代码体积较小

    动态库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。带来好处的同时,也会有问题!如经典的DLL Hell问题,关于如何规避动态库管理问题,可以自行查找相关资料。


    http://www.cnblogs.com/skynet/p/3372855.html

    展开全文
  • 这次分享的宗旨是——让大家学会创建与使用静态库动态库,知道静态库动态库的区别,知道使用的时候如何选择。这里不深入介绍静态库动态库的底层格式,内存布局等,有兴趣的同学,推荐一本书《程序员的自我修养...

    这次分享的宗旨是——让大家学会创建与使用静态库、动态库,知道静态库与动态库的区别,知道使用的时候如何选择。这里不深入介绍静态库、动态库的底层格式,内存布局等,有兴趣的同学,推荐一本书《程序员的自我修养——链接、装载与库》。

    1、一个程序从源文件编译生成可执行文件的步骤:

    预编译 -->  编译 -->  汇编 --> 链接

    (1)预编译,即预处理,主要处理在源代码文件中以“#”开始的预编译指令,如宏展开、处理条件编译指令、处理#include指令等。

    (2)编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析以及优化后生成相应的汇编代码文件。

    (3)汇编是将汇编代码转变成二进制文件。

    (4)链接将二进制文件链接成一个可执行的命令,主要是把分散的数据和代码收集并合成一个单一的可加载并可执行的的文件。链接可以发生在代码静态编译、程序被加载时以及程序执行时。链接过程的主要工作是符号解析和重定位。

    2、库 

    库是一组目标文件的包,就是一些最常用的代码编译成目标文件后打包存放。而最常见的库就是运行时库(Runtime Library),如C运行库CRT.

    库一般分为两种:静态库(.a 、.lib)动态库(.so 、.dll )所谓静态、动态是指链接过程。 

    3、静态库与动态库

    区别:

    (1)lib是编译时用到的,dll是运行时用到的。如果要完成源代码的编译,只需要lib;如果要使动态链接的程序运行起来,只需要dll。
    (2)如果有dll文件,那么lib一般是一些索引信息,记录了dll中函数的入口和位置,dll中是函数的具体内容;如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。使用静态编译的lib文件,在运行程序时不需要再挂动态库,缺点是导致应用程序比较大,而且失去了动态库的灵活性,发布新版本时要发布新的应用程序才行。
    (3)动态链接的情况下,有两个文件:一个是LIB文件,一个是DLL文件。LIB包含被DLL导出的函数名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到DLL文件。在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中相应函数代码的地址,从而节省了内存资源。DLL和LIB文件必须随应用程序一起发行,否则应用程序会产生错误。如果不想用lib文件或者没有lib文件,可以用WIN32 API函数LoadLibrary、GetProcAddress装载。

    ------这里主要讲动态库的优点特性。--------

    静态库:函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其它模块组合起来创建最终的可执行文件(.EXE文件)。
    在使用动态库的时候,往往提供两个文件:一个引入库和一个DLL。引入库包含被DLL导出的函数和变量的符号名,DLL包含实际的函数和数据。在编译链接可执行文件时,只需要链接引入库,DLL中的函数代码和数据并不复制到可执行文件中,在运行的时候,再去加载DLL,访问DLL中导出的函数。

    静态库有两个重大缺点:

    1)空间浪费

    2)静态链接对程序的更新、部署和发布会带来很多麻烦。一旦程序中有任何模块更新,整个程序就要重新链接,发布给用户。

    动态链接的基本思想:把程序按照模块拆分成各个相对独立的部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是想静态链接一样把所有的程序模块都链接成一个单独的可执行文件。

    特点:

    1)代码共享,所有引用该动态库的可执行目标文件共享一份相同的代码与数据。

    2)程序升级方便,应用程序不需要重新链接新版本的动态库来升级,理论上只要简单地将旧的目标文件覆盖掉。

    3)在运行时可以动态地选择加载各种应用程序模块


    什么是库

    库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常

    本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:静态库(.a.lib)和动态库(.so.dll)。

    所谓静态、动态是指链接。回顾一下,将一个程序编译成可执行程序的步骤:

     

    图:编译过程

    静态库

    之所以成为【静态库】,是因为在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。

    试想一下,静态库与汇编生成的目标文件一起链接为可执行文件,那么静态库必定跟.o文件格式相似。其实一个静态库可以简单看成是一组目标文件(.o/.obj文件)的集合,即很多目标文件经过压缩打包后形成的一个文件。静态库特点总结:

    l  静态库对函数库的链接是放在编译时期完成的。

    l  程序在运行时与函数库再无瓜葛,移植方便。

    l  浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。

    下面编写一些简单的四则运算C++类,将其编译成静态库给他人用,头文件如下所示:

    StaticMath.h头文件

    #pragma once

    class StaticMath

    {

    public:

        StaticMath(void);

        ~StaticMath(void);

     

        static double add(double a, double b);//加法

        static double sub(double a, double b);//减法

        static double mul(double a, double b);//乘法

        static double div(double a, double b);//除法

     

        void print();

    };

     

    Linux下使用ar工具、Windowsvs使用lib.exe,将目标文件压缩到一起,并且对其进行编号和索引,以便于查找和检索。一般创建静态库的步骤如图所示:

     

    图:创建静态库过程

    Linux下创建与使用静态库

    Linux静态库命名规则

    Linux静态库命名规范,必须是"lib[your_library_name].a"lib为前缀,中间是静态库名,扩展名为.a

    创建静态库(.a

    通过上面的流程可以知道,Linux创建静态库过程如下:

    l  首先,将代码文件编译成目标文件.oStaticMath.o

    g++ -c StaticMath.cpp

    注意带参数-c,否则直接编译为可执行文件

    l  然后,通过ar工具将目标文件打包成.a静态库文件

    ar -crv libstaticmath.a StaticMath.o

    生成静态库libstaticmath.a

     

    大一点的项目会编写makefile文件(CMake等等工程管理工具)来生成静态库,输入多个命令太麻烦了。

    使用静态库

    编写使用上面创建的静态库的测试代码:

    测试代码:

    #include "StaticMath.h"

    #include <iostream>

    using namespace std;

     

    int main(int argccharargv[])

    {

        double a = 10;

        double b = 2;

     

        cout << "a + b = " << StaticMath::add(a, b) << endl;

        cout << "a - b = " << StaticMath::sub(a, b) << endl;

        cout << "a * b = " << StaticMath::mul(a, b) << endl;

        cout << "a / b = " << StaticMath::div(a, b) << endl;

     

        StaticMath sm;

        sm.print();

     

        system("pause");

        return 0;

    }

    Linux下使用静态库,只需要在编译的时候,指定静态库的搜索路径(-L选项)、指定静态库名(不需要lib前缀和.a后缀,-l选项)。

    # g++ TestStaticLibrary.cpp -L../StaticLibrary -lstaticmath

     

    l  -L:表示要连接的库所在目录

    l  -l:指定链接时需要的动态库,编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.a.so来确定库的名称。

     

    Windows下创建与使用静态库

    创建静态库(.lib

    如果是使用VS命令行生成静态库,也是分两个步骤来生成程序:

    l  首先,通过使用带编译器选项 /c  Cl.exe 编译代码 (cl /c StaticMath.cpp),创建名为“StaticMath.obj”的目标文件。

    l  然后,使用库管理器 Lib.exe 链接代码 (lib StaticMath.obj),创建静态库StaticMath.lib

    当然,我们一般不这么用,使用VS工程设置更方便。创建win32控制台程序时,勾选静态库类型;打开工程属性面板è配置属性è常规,配置类型选择静态库。

     

    图:vs静态库项目属性设置

    Build项目即可生成静态库。

    使用静态库

    测试代码Linux下面的一样。有3种使用方法:

    方法一:

    VS中使用静态库方法:

    l  工程属性面板è通用属性è “框架和引用è添加引用,将显示添加引用对话框。 “项目选项卡列出了当前解决方案中的各个项目以及可以引用的所有库。 项目选项卡中,选择 StaticLibrary 单击确定

     

    l  添加StaticMath.h 头文件目录,必须修改包含目录路径。打开工程属性面板è配置属性è “C/C++”è” 常规,在附加包含目录属性值中,键入StaticMath.h 头文件所在目录的路径或浏览至该目录。

     

    编译运行OK

     

    图:静态库测试结果(vs

    如果引用的静态库不是在同一解决方案下的子工程,而是使用第三方提供的静态库lib和头文件,上面的方法设置不了。还有2中方法设置都可行。

    方法二:

    打开工程属性面板è配置属性è “链接器è命令行,输入静态库的完整路径即可。

     

    方法三:

    l  属性面板è配置属性è “链接器è常规,附加依赖库目录中输入,静态库所在目录;

    l  属性面板è配置属性è “链接器è输入,附加依赖库中输入静态库名StaticLibrary.lib

     

    动态库

    通过上面的介绍发现静态库,容易使用和理解,也达到了代码复用的目的,那为什么还需要动态库呢?

    为什么还需要动态库?

    为什么需要动态库,其实也是静态库的特点导致。

    l  空间浪费是静态库的一个问题。

     

    l  另一个问题是静态库对程序的更新、部署和发布页会带来麻烦。如果静态库liba.lib更新了,所以使用它的应用程序都需要重新编译、发布给用户(对于玩家来说,可能是一个很小的改动,却导致整个程序重新下载,全量更新)。

    动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新

     

    动态库特点总结:

    l  动态库把对一些库函数的链接载入推迟到程序运行的时期。

    l  可以实现进程之间的资源共享。(因此动态库也称为共享库)

    l  将一些程序升级变得简单。

    l  甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)。

    WindowLinux执行文件格式不同,在创建动态库的时候有一些差异。

    l  Windows系统下的执行文件格式是PE格式,动态库需要一个DllMain函数做出初始化的入口,通常在导出函数的声明时需要有_declspec(dllexport)关键字

    l  Linuxgcc编译的执行文件默认是ELF格式,不需要初始化入口,亦不需要函数做特别的声明,编写比较方便。

    与创建静态库不同的是,不需要打包工具(arlib.exe),直接使用编译器即可创建动态库。

    Linux下创建与使用动态库

     

    linux动态库的命名规则

    动态链接库的名字形式为 libxxx.so,前缀是lib,后缀名为“.so”。

    l  针对于实际库文件,每个共享库都有个特殊的名字“soname”。在程序启动后,程序通过这个名字来告诉动态加载器该载入哪个共享库。

    l  在文件系统中,soname仅是一个链接到实际动态库的链接。对于动态库而言,每个库实际上都有另一个名字给编译器来用。它是一个指向实际库镜像文件的链接文件(lib+soname+.so)。

    创建动态库(.so

    编写四则运算动态库代码:

    DynamicMath.h头文件

    #pragma once

    class DynamicMath

    {

    public:

            DynamicMath(void);

            ~DynamicMath(void);

     

            static double add(double a, double b);//¼Ó·¨

            static double sub(double a, double b);//¼õ·¨

            static double mul(double a, double b);//³Ë·¨

            static double div(double a, double b);//³ý·¨

            void print();

    };

     

    l  首先,生成目标文件,此时要加编译器选项-fpic

    g++ -fPIC -c DynamicMath.cpp

    -fPIC 创建与地址无关的编译程序(picposition independent code),是为了能够在多个应用程序间共享。

    l  然后,生成动态库,此时要加链接器选项-shared

    g++ -shared -o libdynmath.so DynamicMath.o

    -shared指定生成动态链接库。

     

    其实上面两个步骤可以合并为一个命令:

    g++ -fPIC -shared -o libdynmath.so DynamicMath.cpp

     

    使用动态库

    编写使用动态库的测试代码:

    测试代码:

    #include "../DynamicLibrary/DynamicMath.h"

     

    #include <iostream>

    using namespace std;

     

    int main(int argc, char* argv[])

    {

        double a = 10;

        double b = 2;

     

        cout << "a + b = " << DynamicMath::add(a, b) << endl;

        cout << "a - b = " << DynamicMath::sub(a, b) << endl;

        cout << "a * b = " << DynamicMath::mul(a, b) << endl;

        cout << "a / b = " << DynamicMath::div(a, b) << endl;

     

        DynamicMath dyn;

        dyn.print();

        return 0;

    }

    引用动态库编译成可执行文件(跟静态库方式一样):

    g++ TestDynamicLibrary.cpp -L../DynamicLibrary -ldynmath

    然后运行:./a.out,发现竟然报错了!!!

     

    可能大家会猜测,是因为动态库跟测试程序不是一个目录,那我们验证下是否如此:

     

    发现还是报错!!!那么,在执行的时候是如何定位共享库文件的呢?

    1)        当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径。此时就需要系统动态载入器(dynamic linker/loader)

    2)        对于elf格式的可执行程序,是由ld-linux.so*来完成的,它先后搜索elf文件的 DT_RPATH段—环境变量LD_LIBRARY_PATH/etc/ld.so.cache文件列表—/lib/,/usr/lib 目录找到库文件后将其载入内存。

    如何让系统能够找到它:

    l  如果安装在/lib或者/usr/lib下,那么ld默认能够找到,无需其他操作。

    l  如果安装在其他目录,需要将其添加到/etc/ld.so.cache文件中,步骤如下:

    n  编辑/etc/ld.so.conf文件,加入库文件所在目录的路径

    n  运行ldconfig ,该命令会重建/etc/ld.so.cache文件

    我们将创建的动态库复制到/usr/lib下面,然后运行测试程序。

     

    Windows下创建与使用动态库

    创建动态库(.dll

    Linux相比,在Windows系统下创建动态库要稍微麻烦一些。首先,需要一个DllMain函数做出初始化的入口(创建win32控制台程序时,勾选DLL类型会自动生成这个文件):

    dllmain.cpp入口文件

    // dllmain.cpp : Defines the entry point for the DLL application.

    #include "stdafx.h"

     

    BOOL APIENTRY DllMain( HMODULE hModule,

                           DWORD  ul_reason_for_call,

                           LPVOID lpReserved

                         )

    {

        switch (ul_reason_for_call)

        {

        case DLL_PROCESS_ATTACH:

        case DLL_THREAD_ATTACH:

        case DLL_THREAD_DETACH:

        case DLL_PROCESS_DETACH:

            break;

        }

        return TRUE;

    }

    通常在导出函数的声明时需要有_declspec(dllexport)关键字:

    DynamicMath.h头文件

    #pragma once

    class DynamicMath

    {

    public:

        __declspec(dllexport) DynamicMath(void);

        __declspec(dllexport) ~DynamicMath(void);

     

        static __declspec(dllexportdouble add(double a, double b);//加法

        static __declspec(dllexportdouble sub(double a, double b);//减法

        static __declspec(dllexportdouble mul(double a, double b);//乘法

        static __declspec(dllexportdouble div(double a, double b);//除法

     

        __declspec(dllexportvoid print();

    };

    生成动态库需要设置工程属性,打开工程属性面板è配置属性è常规,配置类型选择动态库。

     

    图:v动态库项目属性设置

    Build项目即可生成动态库。

    使用动态库

    创建win32控制台测试程序:

    TestDynamicLibrary.cpp测试程序

    #include "stdafx.h"

    #include "DynamicMath.h"

     

    #include <iostream>

    using namespace std;

     

    int _tmain(int argc_TCHARargv[])

    {

        double a = 10;

        double b = 2;

     

        cout << "a + b = " << DynamicMath::add(a, b) << endl;

        cout << "a - b = " << DynamicMath::sub(a, b) << endl;

        cout << "a * b = " << DynamicMath::mul(a, b) << endl;

        cout << "a / b = " << DynamicMath::div(a, b) << endl;

     

        DynamicMath dyn;

        dyn.print();

     

        system("pause");

        return 0;

    }

    方法一:

    l  工程属性面板è通用属性è “框架和引用è添加引用,将显示添加引用对话框。项目选项卡列出了当前解决方案中的各个项目以及可以引用的所有库。 项目选项卡中,选择 DynamicLibrary 单击确定

     

    l  添加DynamicMath.h 头文件目录,必须修改包含目录路径。打开工程属性面板è配置属性è “C/C++”è” 常规,在附加包含目录属性值中,键入DynamicMath.h 头文件所在目录的路径或浏览至该目录。

     

    编译运行OK

     

    图:动态库测试结果(vs

    方法二:

    l  属性面板è配置属性è “链接器è常规,附加依赖库目录中输入,动态库所在目录;

     

    l  属性面板è配置属性è “链接器è输入,附加依赖库中输入动态库编译出来的DynamicLibrary.lib

     

    这里可能大家有个疑问,动态库怎么还有一个DynamicLibrary.lib文件?即无论是静态链接库还是动态链接库,最后都有lib文件,那么两者区别是什么呢?其实,两个是完全不一样的东西。

     

    StaticLibrary.lib的大小为190KBDynamicLibrary.lib的大小为3KB,静态库对应的lib文件叫静态库,动态库对应的lib文件叫【导入库】。实际上静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息

    动态库的显式调用

    上面介绍的动态库使用方法和静态库类似属于隐式调用,编译的时候指定相应的库和查找路径。其实,动态库还可以显式调用。【在C语言中】,显示调用一个动态库轻而易举!

    Linux下显式调用动态库

    #include <dlfcn.h>,提供了下面几个接口:

    l  void * dlopen( const char * pathname, int mode ):函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。

    l  void* dlsym(void* handle,const char* symbol)dlsym根据动态链接库操作句柄(pHandle)与符号(symbol),返回符号对应的地址。使用这个函数不但可以获取函数地址,也可以获取变量地址。

    l  int dlclose (void *handle)dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0,才会真正被系统卸载。

    l  const char *dlerror(void):当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。

    Windows下显式调用动态库

    应用程序必须进行函数调用以在运行时显式加载 DLL。为显式链接到 DLL,应用程序必须:

    l  调用 LoadLibrary(或相似的函数)以加载 DLL 和获取模块句柄。

    l  调用 GetProcAddress,以获取指向应用程序要调用的每个导出函数的函数指针。由于应用程序是通过指针调用 DLL 的函数,编译器不生成外部引用,故无需与导入库链接。

    l  使用完 DLL 后调用 FreeLibrary

    显式调用C++动态库注意点

    C++来说,情况稍微复杂。显式加载一个C++动态库的困难一部分是因为C++name mangling另一部分是因为没有提供一个合适的API来装载类,在C++中,您可能要用到库中的一个类,而这需要创建该类的一个实例,这不容易做到。

    name mangling可以通过extern "C"解决。C++有个特定的关键字用来声明采用C binding的函数:extern "C" 。用 extern "C"声明的函数将使用函数名作符号名,就像C函数一样。因此,只有非成员函数才能被声明为extern "C",并且不能被重载。尽管限制多多,extern "C"函数还是非常有用,因为它们可以象C函数一样被dlopen动态加载。冠以extern "C"限定符后,并不意味着函数中无法使用C++代码了,相反,它仍然是一个完全的C++函数,可以使用任何C++特性和各种类型的参数。

    另外如何从C++动态库中获取类,附上几篇相关文章,但我并不建议这么做:

    l  LoadLibrary调用DLL中的Class》:http://www.cppblog.com/codejie/archive/2009/09/24/97141.html

    l  C++ dlopen mini HOWTO》:http://blog.csdn.net/denny_233/article/details/7255673

    “显式”使用C++动态库中的Class是非常繁琐和危险的事情,因此能用“隐式”就不要用“显式”,能静态就不要用动态。

    附件:Linux下库相关命令

    g++(gcc)编译选项

    l  -shared :指定生成动态链接库。

    l  -static :指定生成静态链接库。

    l  -fPIC :表示编译为位置独立的代码,用于编译共享库。目标文件需要创建成位置无关码, 念上就是在可执行程序装载它们的时候,它们可以放在可执行程序的内存里的任何地方。

    l  -L. :表示要连接的库所在的目录。

    l  -l:指定链接时需要的动态库。编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.a/.so来确定库的名称。

    l  -Wall :生成所有警告信息。

    l  -ggdb :此选项将尽可能的生成gdb 的可以使用的调试信息。

    l  -g :编译器在编译的时候产生调试信息。

    l  -c :只激活预处理、编译和汇编,也就是把程序做成目标文件(.o文件

    l  -Wl,options :把参数(options)传递给链接器ld 。如果options 中间有逗号,就将options分成多个选项,然后传递给链接程序。

    nm命令

    有时候可能需要查看一个库中到底有哪些函数,nm命令可以打印出库中的涉及到的所有符号。库既可以是静态的也可以是动态的。nm列出的符号有很多,常见的有三种:

    l  一种是在库中被调用,但并没有在库中定义(表明需要其他库支持),用U表示;

    l  一种是库中定义的函数,用T表示,这是最常见的;

    l  一种是所谓的弱态”符号,它们虽然在库中被定义,但是可能被其他库中的同名符号覆盖,用W表示。

    $nm libhello.h

    ldd命令

    ldd命令可以查看一个可执行程序依赖的共享库,例如我们编写的四则运算动态库依赖下面这些库:

     

    总结

    二者的不同点在于代码被载入的时刻不同

    l  静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库,因此体积较大

    l  动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在,因此代码体积较小

    动态库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。带来好处的同时,也会有问题!如经典的DLL Hell问题,关于如何规避动态库管理问题,可以自行查找相关资料。

     

    展开全文
  • 静态库/动态库概要在Windows下静态库的后缀为:.lib、动态库后缀为:.dll;而在Linux下静态库的后缀为:.a、动态库的后缀为:.os。那么什么是静态库呢?首先我们来看看程序编译的大体流程:预处理——编译——汇编...
  • 转自; ...     1 什么是库 ...库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。...库有两种:静态库(.a、.lib)...
  • LInux下理解C++编译使用动态连接库,静态库,为了更好的理解作为例子说明,以glog日志库为例说明:// 下面是文件test.cpp,调用glog库打印日志的例子#include int main ( int argc , char *c[] ) { FLAGS_log_dir ...
  • 编译某个
  • 根据GenDll.cpp文件,分别生成动态库.so和静态库...使用ar命令生成.a文件,可参考:Linux下动态库(.so)和静态库(.a)# 1、准备工作,编译方式、目标文件名、依赖库路径的定义。 CC = g++ CFLAGS := -Wall -O3 -std=c++0x
  • window平台下动态库静态库生成与使用C++动态库动态库的生成vs的设置自行百度,废话不多讲,上代码:代码参考:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=29121609&id=3851844//add.h ...
  •  本文仅仅是总结了,静态库动态库使用及基本编译方法。至于库本身的格式,内存地址等较底层问题,则等有时间放到操作系统一类的文章中研究。下面开始正题吧。 二.介绍  从源代码到可执行程序,通常要...
  • 原文:...看到一篇介绍静态链接库动态链接库的文章,写的太好了,遂转载过来分享一下。 这次分享的宗旨是——让大家学会创建与使用静态库动态库,知道静态库动态库的区别,知道...
  • 近期正在做在Ubuntu环境下将C++程序封装成动态库文件和静态库文件,期间不知道掉了多少坑,在这里写下来以免忘记工作是将实现MTCNN的C++源代码封装成动态库文件和静态库文件,原先在github上找到了一个依据opencv...
  • 最近研究了一下动态库和静态库调用的问题,本篇文字是在生成动态库的时候调用静态库,并使用动态库编译成可执行程序。不足之处还望多多指教。 1、首先写了一个简单的接口编译成静态库(.h文件.cpp文件如下) //...
  • 本视频课程介绍C语言中动态库和静态库的基本概念,制作和使用方法,手工加载动态库方法,如何导出函数类,以及VC的静态编译等等
  • 创建MFC项目时,选择动态库和静态库的区别动态库在VS2017版本中,选择的时候是叫“在共享DLL中使用MFC”。这样创建的时候生成的*.exe文件一般较小,原因有几点1. 此*.exe程序内部,一般不会包含文件所需要的库,其...
  • 我创建了一个c++静态库(比如a.lib),然后想引用到另一个项目b中去,这个项目如果设置同样生成静态库,就正常编译后生成b.lib,但是我需要的是动态库(b.dll),然而当我将属性页配置类型改为动态库再编译时提示...
1 2 3 4 5 ... 20
收藏数 113,207
精华内容 45,282