欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

静态链接库、动态链接库使用方法

程序员文章站 2022-06-25 19:20:22
...

 总结一下动态链接库和静态链接库。

首先搞清楚几个概念:

静态库:函数和数据被编译进一个二进制文件,通常扩展名为.lib。在使用静态库的情况下,在编译连接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其他模块组合起来创建最终的可执行文件(.exe文件)。

动态库:在使用动态库的时候,往往提供两个文件:1.引入库(.lib)和.dll。引入库包含被Dll导出的函数和变量的符号名,Dll包含实际的函数和数据。在编译链接可执行文件时,只需要链接引入库,Dll中的函数代码和数据并不复制到可执行文件中,在运行的时候,再去加载Dll,访问Dll中导出的函数。

静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要其它的库文件;动态链接就是把调用的函数所在文件模块(Dll)和调用函数在文件中的位置等信息链接进目标程序,程序运行的时候再从Dll中寻找相应函数代码,因此需要相应Dll文件的支持。

Lib:静态链接库和动态链接库都会生成.lib文件,但.lib文件却有着不同的内容。

目标库(Object Libraries)

目标库又叫静态链接库,是扩展名为.LIB的文件,包括了用户程序要用到 的各种函数。它在用户程序进行链接时,“静态链接”到可执行程序文件当中。例如,在VC++中最常使用到的C运行时目标库文件就是LIBC.LIB。在链 接应用程序时常使用所谓“静态链接”的方法,即将各个目标文件(.obj)、运行时函数库(.lib)以及已编译的资源文件(.res)链接到一起,形成 一个可执行文件(.exe)。使用静态链接时,可执行文件需要使用的各种函数和资源都已包含到文件中。这样做的缺点是对于多个程序都使用的相同函数和资源 要重复链接到exe文件中,使程序变大、占用内存增加。  

导入库(Import Libraries)

导入库是一种特殊形式的目标库文件形式。和目标库文件一样,导入库文件的扩展名也是.LIB,也是在用户程序被链接时,被“静态链接”到可执行文件当中。但是不同的是,导入库文件中并不包含有程序代码。相应的,它包含了相关的链接信息,帮助应用程序在可执行文件中建立起正确的对应于动态链接库的重定向表。比如KERNEL32.LIB、USER32.LIB和GDI32.LIB就是我们常用到的导入库,通过它们,我们就可以调用Windows提供的函数了。 如果我们在程序中使用到了Rectangle这个函数,GDI32.LIB就可以告诉链接器,这个函数在GDI32.DLL动态链接库文件中。这样,当用 户程序运行时,它就知道“动态链接”到GDI32.DLL模块中以使用这个函数。其实说白了导入库就是一个索引,一个dll动态链接库的索引表,这是我的 理解。

Dll:

“动态链接”是将一些公用的函数或资源组织成动态链接库文件(.dll),当某个需要使用dll中的函数或资源的程序启动时(准确的说是初始化时),系统将该 dll映射到调用进程的虚拟地址空间、增加该dll的引用计数值,然后当实际使用到该dll时操作系统就将该dll载入内存;如果使用该dll的所有程序 都已结束,则系统将该库从内存中移除。使用同一dll的各个进程在运行时共享dll的代码,但是对于dll中的数据则各有一份拷贝(当然也有在dll*享数据的方法)。 动态链接库中可以定义两种函数:输出函数和内部函数。输出函数可以被其他模块调用,内部函数只能被动态链接库本身调用。动态链接库也可以输出数据,但这些数据通常只被它自己的函数所使用。

.h文件:头文件,变量及函数声明就在.h文件中。

.pdb文件:用来帮助软件进行调试的,无论是静态链接库、动态链接库的静态调用、动态调用只要有.pdb文件就能进行调试。

extern "C":不发生名字改编,解决C++和C语言之间调用时名字改编的问题,加上之后不改变函数名。extern "C"只能用来导出全局函数,不能导出类和类的成员函数。我想大概是因为C中没有类和类的成员变量吧。

__declspec(dllexport):表示该函数为DLL输出函数,即其他应用程序可以调用该函数


一、静态链接库

静态链接库只生成.lib文件,使用的时候直接用头文件和.lib文件即可。

头文件:

#pragma once
int Add(int a, int b);

int Sub(int a, int b);

class Student
{
public:
    virtual char *GetName() const;
    virtual void SetName(char *Name);
    virtual int  GetAge() const;
    virtual void SetAge(int age);
    virtual void Output();

private:
    char *m_strName;
    int   m_nAge;

};

源程序:

#include "stdafx.h"
#include "Sample.h"
#include <iostream>

using namespace std;

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

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


char * Student::GetName() const
{
    return m_strName;
}

void Student::SetName(char * Name)
{
    m_strName = Name;
}

int Student::GetAge() const
{
    return m_nAge;
}

void Student::SetAge(int age)
{
    m_nAge = age;
}

void Student::Output()
{
    std::cout << "Name: " << m_strName << std::endl << "Age: " << m_nAge << std::endl;
}


测试程序:

#include "stdafx.h"
#include <iostream>
#include "Sample.h"

using namespace std;

int main()
{
    
    int sum = 0;
    int a = 5;
    int b = 3;
    sum = Add(a, b);
    cout << "sum = a + b = " << sum << endl;
    int diff = 0;
    diff = Sub(a, b);
    cout << "diff = a - b = " << diff << endl;

    Student xiaoming;
    xiaoming.SetAge(27);
    xiaoming.SetName("xiaoming");
    xiaoming.Output();
    int age = xiaoming.GetAge();
    char *name = xiaoming.GetName();
    cout << "Name:" << name << endl << "Age:" << age << endl;
    
    system("pause");
    return 0;
}



二、动态链接库

生成动态连接库的程序是一致的,但不同是调用方式,动态链接库有动态调用和静态调用两种方式。

导出函数的时候,两者可以一样,都使用

#ifdef DLL_IMPLEMENT
#define DLL_API extern "C" __declspec(dllexport)
#else
#define DLL_API extern "C" __declspec(dllimport)
#endif

这个宏。其中个的extern “C”是为了解决C++名字改编的问题,在动态调用时比较有用,因为动态调用需要根据函数名来获取函数地址,如果不加这个,动态调用的函数名将会发生改编,将找不到函数;静态连接就不需要这个。

导出类就不同了,导出类在静态调用动态链接库的情况下,可以直接用__declspec(dllexport)导出类,此时这个类就能正常使用了。在动态调用的情况下,这样直接导出的类是没有地址的,用不了,且它的名字也会发生改变(个人看法)。我一般的用法是使用一个Create函数,Create里面是return new CClass,即这个类的实例化,函数类型是CClass *,整个函数可以理解为CClass class = new CClass。当然动态调用的时候加上了extern “C”,这样可以通过函数名找到函数地址。此时类的成员变量仍然不能用,因为成员函数都没有地址,找不到,因为我不能通过这个类来找到它成员函数的地址,这里把成员函数声明为虚函数,这样就可以了。因为虚函数在类中有一个虚函数表,通过这个虚函数表可以找到类中所有虚函数的地址,这就是我动态调用Dll导出类的方法。

1. 动态链接库的静态调用


头文件文件:需要在预编译器里面定义宏DLL_IMPLEMENT

#pragma once

#ifdef DLL_IMPLEMENT
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif // DLL_API

DLL_API int Add(int a, int b);

DLL_API int Sub(int a, int b);

class DLL_API Student
{
public:
    char *GetName() const;
    void SetName(char *Name);
    int  GetAge() const;
    void SetAge(int age);
    void Output();

private:
    char *m_strName;
    int   m_nAge;

};
源文件:

#include "stdafx.h"
#include <iostream>
#include "Sample.h"

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

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

char * Student::GetName() const
{
    return m_strName;
}

void Student::SetName(char * Name)
{
    m_strName = Name;
}

int Student::GetAge() const
{
    return m_nAge;
}

void Student::SetAge(int age)
{
    m_nAge = age;
}

void Student::Output()
{
    std::cout << "Name: " << m_strName << std::endl << "Age: " << m_nAge << std::endl;
}


测试文件:
// testStaticDll.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include "sample.h"


using namespace std;

int main()
{
    int sum = 0;
    int diff = 0;
    int a = 5;
    int b = 3;
    sum = Add(a, b);
    diff = Sub(a, b);
    cout << "sum = a + b = " << sum << endl;
    cout << "diff = a - b = " << diff << endl;
    ////////////////////////////分隔符////////////////////////////////////
    
    Student xiaoming;
    xiaoming.SetAge(27);
    xiaoming.SetName("xiaoming");
    xiaoming.Output();
    char *sName = xiaoming.GetName();
    int  nAge = xiaoming.GetAge();
    cout << "Name: " << sName << endl << "Age: " << nAge << endl;
    
    system("pause");
    return 0;
}


2. 动态链接库的动态调用

头文件

#pragma once

#ifdef DLL_IMPLEMENT
#define DLL_API extern "C" __declspec(dllexport)
#else
#define DLL_API extern "C" __declspec(dllimport)
#endif 

DLL_API int Add(int a, int b);

DLL_API int Sub(int a, int b);

class Student
{
public:
    virtual char *GetName() const;
    virtual void SetName(char *Name);
    virtual int  GetAge() const;
    virtual void SetAge(int age);
    virtual void Output();

private:
    char *m_strName;
    int   m_nAge;

};

DLL_API Student *Create();

源文件:

#include "stdafx.h"
#include <iostream>
#include "Sample.h"

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

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

Student * Create()
{
    return new Student;
}

char * Student::GetName() const
{
    return m_strName;
}

void Student::SetName(char * Name)
{
    m_strName = Name;
}

int Student::GetAge() const
{
    return m_nAge;
}

void Student::SetAge(int age)
{
    m_nAge = age;
}

void Student::Output()
{
    std::cout << "Name: " << m_strName << std::endl << "Age: " << m_nAge << std::endl;
}

测试文件:

// testDynamicDll.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "windows.h"
#include "sample.h"
#include <iostream>

using namespace std;
typedef int(*Fun)(int , int );
typedef Student *(*CreateFunc)();
int main()
{
    HINSTANCE hIns = NULL;
    hIns = LoadLibrary(_T("DllFunc.dll"));
    if (hIns == NULL)
    {
        cout << "Dll加载失败" << endl;
        system("pause");
        return 0;
    }
    Fun Sum = (Fun)GetProcAddress(hIns, "Add");
    int sum = 0;
    int a = 5;
    int b = 3;
    sum = Sum(a, b);
    cout << "sum = a + b = " << sum << endl;

    Fun Diff = (Fun)GetProcAddress(hIns, "Sub");
    int diff = 0;
    diff = Diff(a, b);
    cout << "diff = a - b = " << diff << endl;

    CreateFunc CreateStudent = (CreateFunc)GetProcAddress(hIns, "Create");
    Student *xiaoming = CreateStudent();
    xiaoming->SetAge(27);
    xiaoming->SetName("xiaoming");
    xiaoming->Output();
    int age = xiaoming->GetAge();
    char *name = xiaoming->GetName();
    cout << "Name:" << name << endl << "Age:" << age << endl;
    FreeLibrary(hIns);
    system("pause");
    return 0;
}