1. 程式人生 > >C++靜態庫與動態庫詳解與使用

C++靜態庫與動態庫詳解與使用

這次分享的宗旨是——讓大家學會建立與使用靜態庫、動態庫,知道靜態庫與動態庫的區別,知道使用的時候如何選擇。這裡不深入介紹靜態庫、動態庫的底層格式,記憶體佈局等,有興趣的同學,推薦一本書《程式設計師的自我修養——連結、裝載與庫》。

什麼是庫

庫是寫好的現有的,成熟的,可以複用的程式碼。現實中每個程式都要依賴很多基礎的底層庫,不可能每個人的程式碼都從零開始,因此庫的存在意義非同尋常

本質上來說庫是一種可執行程式碼的二進位制形式,可以被作業系統載入記憶體執行。庫有兩種:靜態庫(.a.lib)和動態庫(.so.dll)。

所謂靜態、動態是指連結。回顧一下,將一個程式編譯成可執行程式的步驟:

clip_image002[4]

圖:編譯過程

靜態庫

之所以成為【靜態庫】,是因為在連結階段,會將彙編生成的目標檔案.o與引用到的庫一起連結打包到可執行檔案中。因此對應的連結方式稱為靜態連結。

試想一下,靜態庫與彙編生成的目標檔案一起連結為可執行檔案,那麼靜態庫必定跟.o檔案格式相似。其實一個靜態庫可以簡單看成是一組目標檔案(.o/.obj檔案)的集合,即很多目標檔案經過壓縮打包後形成的一個檔案。靜態庫特點總結:

l靜態庫對函式庫的連結是放在編譯時期完成的。

l程式在執行時與函式庫再無瓜葛,移植方便。

l浪費空間和資源,因為所有相關的目標檔案與牽涉到的函式庫被連結合成一個可執行檔案。

下面編寫一些簡單的四則運算C++類,將其編譯成靜態庫給他人用,標頭檔案如下所示:

StaticMath.h標頭檔案

#pragmaonce

classStaticMath

{

public:

    StaticMath(void);

    ~StaticMath(void);

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

staticdouble sub(double a, double b);//減法

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

staticdouble 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>

usingnamespace std;

int main(intargccharargv[])

{

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執行檔案格式不同,在建立動態庫的時候有一些差異。

lWindows系統下的執行檔案格式是PE格式,動態庫需要一個DllMain函式做出初始化的入口,通常在匯出函式的宣告時需要有_declspec(dllexport)關鍵字

lLinuxgcc編譯的執行檔案預設是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"

BOOLAPIENTRY DllMain( HMODULEhModule,

DWORDul_reason_for_call,

LPVOIDlpReserved

                     )

{

switch (ul_reason_for_call)

    {

caseDLL_PROCESS_ATTACH:

caseDLL_THREAD_ATTACH:

caseDLL_THREAD_DETACH:

caseDLL_PROCESS_DETACH:

break;

    }

returnTRUE;

}

通常在匯出函式的宣告時需要有_declspec(dllexport)關鍵字:

DynamicMath.h標頭檔案

#pragmaonce

classDynamicMath

{

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>

usingnamespace std;

int_tmain(intargc_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"

相關推薦

C/C++開發】C++之enum列舉量宣告、定義、使用列舉類列舉類前置型別宣告

前面講到可以通過強制轉換將其他型別值賦給列舉變數:Weekday = enumType(2);這是合法的;但是Weekday = enumType(20);是非法的。這裡涉及列舉取值範圍的概念:列舉的上限是 大於最大列舉量的 最小的2的冪,減去1; 列舉的下限有兩種情況:一、列舉量的最小值不小於0,則列

centos/linux alternativesupdate-alternatives軟件版本切換

等等 ava 包括 blank 多個 config etc 兩種模式 版權 update-alternatives是linux系統中專門維護系統命令鏈接符的工具,通過它可以很方便的設置系統默認使用哪個命令、哪個軟件版本,比如,我們在系統中同時安裝了open jdk和

EOS虛擬機器智慧合約分析

EOS智慧合約和虛擬機器分析 EOS虛擬機器同經典的EVM,是EOS中執行智慧合約的容器,但是從設計上講它與EOS.IO是分離的。進 一步指令碼語言和虛擬機器的技術設計與EOS.IO分離。從巨集觀來講任何語言或者虛擬機器,只要滿足條件適 合沙盒模式執行,同時滿足一定的執行

linux下redisphpredis安裝簡單操作

首先下載redis安裝包 [[email protected] ~]#wget http://download.redis.io/releases/redis-3.0.2.tar.gz [[email protected] ~]# tar xzf re

讀檔案時輸入流ifstream中函式tellg()seekg()的示例

讀取檔案時有時會需要回退 比如讀完第n行想回退到第n-1行再讀一次n行 或者讀字元時想回退到n個字元之前都可以使用tellg()和seekg() tellg() 用於在輸入流中獲取位置 seekg()用於設定在輸入流中的位置 其實本質上檔案讀寫就是一個指標指向檔案中某個

Linux C 靜態(.a) 動態(.so) 的

庫從本質上來說是一種可執行程式碼的二進位制格式,可以被載入記憶體中執行。庫分靜態庫和動態庫兩種。 一、靜態庫和動態庫的區別 1、靜態函式庫 這類庫的名字一般是libxxx.a;利用靜態函式庫編譯成的檔案比較大--空間,因為整個函式庫的所有資料都會被整合進目的碼中,他的優點就顯而易見了,即編譯後的執行

C++靜態動態使用

這次分享的宗旨是——讓大家學會建立與使用靜態庫、動態庫,知道靜態庫與動態庫的區別,知道使用的時候如何選擇。這裡不深入介紹靜態庫、動態庫的底層格式,記憶體佈局等,有興趣的同學,推薦一本書《程式設計師的自我修養——連結、裝載與庫》。 什麼是庫 庫是寫好的現有的,成熟的,可以複用的程式碼。現實中每個程式都要依

深入探討Linux靜態動態(轉)

share 分享 命名 one .com 過程 程序 簡單介紹 mage 2.生成動態庫並使用 linux下編譯時通過 -shared 參數可以生成動態庫(.so)文件,如下 庫從本質上來說是一種可執行代碼的二進制格式,可以被載入內存中執行。庫分靜態庫和動態庫兩種。

C++靜態動態

name mangling可以通過extern "C"解決。C++有個特定的關鍵字用來宣告採用C binding的函式:extern "C" 。用 extern "C"宣告的函式將使用函式名作符號名,就像C函式一樣。因此,只有非成員函式才能被宣告為extern "C",並且不能被過載。儘管限制多多,exter

Linux下gcc生成和使用靜態動態

一、基本概念 1.1什麼是庫 在windows平臺和linux平臺下都大量存在著庫。 本質上來說庫是一種可執行程式碼的二進位制形式,可以被作業系統載入記憶體執行。 由於windows和linux的平臺不同(主要是編譯器、彙編器和聯結器的不同),因此二者庫的二

NDK 編譯和使用靜態動態; Android.mk 檔案語法; Android.mk高階寫法

===================================================================================== 0. Android.mk簡介: Android.mk檔案用來告知NDK Build 系統關於Source的資訊。 Andro

c++中靜態動態的建立連結

2、動態庫      在使用動態庫的時候,往往提供兩個檔案:一個引入庫(.lib)和一個DLL(.dll)檔案。雖然引入庫的字尾也是.lib ,但是動態庫的引入庫檔案和靜態庫檔案有著本質的區別,對一個DLL來說,其引入庫檔案(.lib)包含DLL匯出的函式和變數的符號名,而.dll檔案包含該DLL實際的函式

c++中使用外部exe,.dll.lib檔案的生成使用的

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

g++ 編譯連結C++程式碼, 生成使用靜態動態

例如我有A.cpp、A.h、main.cpp 三個檔案 編譯連結C++程式碼: 第一步:g++ -c A.cpp main.cpp 這樣就可以編譯A.cpp和main.cpp的程式碼生成A.o和main.o檔案【因為A.cpp包含了A.h的標頭檔案,所以一般編譯時

linux下靜態動態

  原文:http://blog.chinaunix.net/uid-23069658-id-3142046.html 今天我們主要來說說Linux系統下基於動態庫(.so)和靜態(.a)的程式那些貓膩。在這之前,我們需要了解一下原始碼到可執行程式之間到底發生了什麼神奇而

iOS 靜態動態

什麼是庫 ?   庫就是程式程式碼的集合,將N個檔案組織起來,是共享程式程式碼的一種方式。庫從本質上來說是一種可執行程式碼的二進位制格式,可以被載入記憶體中執行。 庫的分類 開源庫:原始碼是公開的,可以看到每個實現檔案(.m檔案)的實現,例如GitHub上的常用的開源庫

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

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

靜態動態的簡單說明

外部程序 endif ldconfig 開始 director 有時 鏈接 現在 con   一.靜態庫和動態庫的簡單介紹   程序設計的模塊化是人們一直在追求的目標,因為當一個系統十分復雜的時候,將系統模塊化既可以並行開發,又可以增強程序的可用性,降低程序間的耦合度。在一

【基於初學者的SSH】struts2 值棧的struts2標簽+ognl表達式

radi ring etl action 值棧 多選 https submit 技術分享 一:什麽是值棧:struts2裏面本身提供的一種存儲機制,類似於域對象,值棧,可以存值和取值  特點:先進後出,最上面的元素叫做棧頂,也叫壓棧。  <s:debug><

linux 靜態動態(共享)的制作使用(註意覆蓋問題)

png 環境變量 src bfile idt 鏈接器 問題 靜態 插入 一、linux操作系統支持的函數庫分支   靜態庫:libxxx.a,在編譯時就將庫編譯進可執行程序     優點:程序的運行環境中不需要外部的函數庫     缺點:可執行程序大   動態庫:又