1. 程式人生 > >靜態連結庫、動態連結庫使用方法

靜態連結庫、動態連結庫使用方法

 總結一下動態連結庫和靜態連結庫。

首先搞清楚幾個概念:

靜態庫:函式和資料被編譯進一個二進位制檔案,通常副檔名為.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;
}



相關推薦

靜態連結動態連結使用方法

 總結一下動態連結庫和靜態連結庫。 首先搞清楚幾個概念: 靜態庫:函式和資料被編譯進一個二進位制檔案,通常副檔名為.lib。在使用靜態庫的情況下,在編譯連線可執行檔案時,連結器從庫中複製這些函式和資料並把它們和應用程式的其他模組組合起來建立最終的可執行檔案(.exe檔案)。

Linux靜態連結動態連結動態載入庫

所謂靜態連結庫,說白了就是在你把寫好的程式碼編譯的時候,就把你引用的庫一起給編進去了,從此後你編出來的執行程式跟外面都不再有任何關係,即使這個庫更新了,你也搭不上邊兒,其次,如果系統中許多類似的程式都需

C++語言--連結串列-7.1----連結串列動態連結串列

前言:我愛程式設計,編程使我快樂!! 我們知道,陣列式計算機根據事先定義好的陣列型別與長度自動為其分配一連續的儲存單元,相同陣列的位置和距離都是固定的,也就是說,任何一個數組元素的地址都可一個簡單的公式計算出來,因此這種結構可以有效的對陣列元素進行隨機訪問。但若對陣列

動態靜態編譯測試:含靜態連結動態靜態動態連結靜態動態

本文的目的是測試各種型別庫的編譯後的使用效果,包括庫又連結其他庫的編譯方法,使用方法,依賴性等。 太長不看版:請跳至文章最後的總結對比表。 一。內容包含: ①靜態庫libbb.a依賴靜態庫libaa.a的測試; ②靜態庫libbb.a依賴動態庫libaa.so的測試;

靜態動態,dll檔案lib檔案,隱式連結顯式連結淺見

靜態連結、動態連結 靜態庫和動態庫分別應用在靜態連結方式和動態連結方式中,所謂靜態連結方式是指在程式執行之前完成所有的連結工作,把靜態庫一起打包合入,生成一個可執行的目標檔案(EXE檔案)。所謂

VS2010與VC++6.0使用靜態動態連結的不同

無論是VS2010,還是VC++6.0,若要使用靜、動態連結庫,都需要提供編譯、連結、執行時所需的檔案(編譯階段要使用連結庫的標頭檔案;連結階段要使用連結庫對應的標頭檔案;對於動態連結庫,在執行時還需要載入dll檔案),只不過二者設定的方式不同而已。 在VS2010中編寫

(轉)靜態編譯,動態編譯,靜態連結動態連結

1.定義 LIB檔案中包含函式程式碼本身,在編譯時直接將程式碼加入程式當中。稱為靜態連結庫static link library。 LIB包含了函式所在的DLL檔案和檔案中函式位置的資訊(入口),程式碼由執行時載入在程序空間中的DLL提供,稱為動態連結庫dynamic link libr

C語言函式動態連結靜態連結

首先,函式庫就是一些事先寫好的函式的集合,是別人分享的,我們可以拿來使用的。經過一些校準和整理,就形成一份標準化的函式庫。例如glibc 函式庫有兩種提供形式:動態連結庫與靜態連結庫 早起函式庫裡的函式都是直接共享的,就是所謂的開源社群。後來函式庫商業化,就出現了靜態連結庫與動態連結庫。

Linux中,.a和.so,其實就是靜態連結動態連結

詳細查了一下,.a與.so的區別,其實就是靜態連結庫與動態連結庫。有一篇博文,很詳細,附上鍊接:http://blog.csdn.net/nieyinyin/article/details/6890557   Linux下的.so是基於Linux下的動態連結,其功能和作用類

windows應用程式【三】靜態連結動態連結

在寫c語言程式時,我們會呼叫標頭檔案 呢麼標頭檔案理論上一定有實現方法 我們可以去檢視標頭檔案一般在你安裝vc目錄下的/vc98/include 來看一下windows.h 但是我們發現windows.h中只是一些定義與宣告 呢麼我們再來看windows.h的標頭檔案 我們開啟wi

15 靜態連線動態連結

靜態連結庫就是一種軟體模組化開發的一種解決方案; VS2013靜態庫的製作簡單示例: 建立Win32專案,然後選擇靜態庫,新增如下兩個簡單檔案,編譯生成靜態庫就可以使用了; /* *add.h */ #ifndef ADD_H #define ADD_H int add(int x,

Linux下使用gcc進行靜態編譯和使用動態連結編譯

/home/plus/demo下有main.c和func.c兩個檔案: func.c: int func(int a) { return a+1; } main.c: #i

使用cmake生成基於靜態動態連結

在軟體開發過程中,有時候需要基於靜態庫生成動態庫檔案。 ld執行時,預設會把靜態庫中不使用的函式過濾掉,導致生成的動態庫檔案不能包含所有的函式。所以需要配置ld的選項--whole-archi

如何使用cmake生成基於靜態動態連結

    在工程搭建時,可能會有將靜態庫連結成動態庫的需求,如出於程式碼保護的角度,某些模組會發布.a副檔名的靜態庫,我們要將多個這樣的靜態庫連結成一個動態庫。但與直接link目標檔案不同的是,ld以預設引數執行時,並把靜態庫中沒有用到的函式過濾掉,導致生成的so並未包含所要

靜態編譯,動態編譯,靜態連結動態連結

1.定義 LIB檔案中包含函式程式碼本身,在編譯時直接將程式碼加入程式當中。稱為靜態連結庫static link library。 LIB包含了函式所在的DLL檔案和檔案中函式位置的資訊(入口),程式碼由執行時載入在程序空間中的DLL提供,稱為動態連結庫dynamic li

C++靜態動態建立及呼叫方法

1 什麼是庫 庫是寫好的現有的,成熟的,可以複用的程式碼。現實中每個程式都要依賴很多基礎的底層庫,不可能每個人的程式碼都從零開始,因此庫的存在意義非同尋常。本質上來說庫是一種可執行程式碼的二進位制形式,可以被作業系統載入記憶體執行。庫有兩種:靜態庫(.a、.lib)和動態

Makefile常用萬能模板(包括靜態鏈接動態鏈接可執行文件)

lib post targe class 到你 param 圖片 spa log 本文把makefile 分成了三份:生成可執行文件的makefile,生成靜態鏈接庫的makefile,生成動態鏈接庫的makefile。   這些makefile都很簡單,一般都是一看就會用

Linux下靜態動態的建立和使用

Linux下靜態庫、動態庫的建立和使用 Linux庫檔名由:字首lib、庫名和字尾3部分組成,靜態庫通常以.a作為字尾,動態庫以.so作為字尾, Linux下把動態庫叫做共享庫,so即shared object的縮寫。 靜態庫是程式編譯連結時使用,動態庫是程式執行時使用。

靜態動態

靜態庫 靜態庫 靜態庫 優點 執行速度快 缺點 佔用系統資源比較多 使用的場合

gcc 編譯工具(下)--- 外部共享靜態動態

gcc 編譯工具(下)— 外部庫、共享庫、靜態庫、動態庫 1. 標頭檔案與庫檔案 在使用C語言和其他語言進行程式設計時,需要標頭檔案來提供對常數的定義和對系統及函式呼叫的宣告。 庫檔案是一些預先編譯好的函式集合,那些函式都是按照可重用原則編寫的。他們通