GCC編譯過程與動態連結庫和靜態連結庫(未整理)
來源:https://www.cnblogs.com/qingjiaowoxiaoxioashou/p/6410588.html
根據連結時期的不同,庫又有靜態庫和動態庫之分。靜態庫是在連結階段被連結的,所以生成的可執行檔案就不受庫的影響,即使庫被刪除,程式依然可以成功執行。而動態庫是在程式執行的時候被連結的。程式執行完,庫仍需保留在系統上,以供程式執行時呼叫。連結靜態庫從某種意義上來說是一種複製貼上,被連結後庫就直接嵌入可執行程式中了,這樣系統空間有很大的浪費,而且一旦發現系統中有bug,就必須一一把連結該庫的程式找出來,然後重新編譯,十分麻煩。而動態庫剛好彌補了這個缺陷,因為動態庫是在程式執行時被連結的,所以磁碟上只需保留一份副本,一次節約了空間,如果發現bug或者是要升級,只要用新的庫把原來的替換掉就可以了。靜態庫是不是一無是處了呢?非也。如果程式碼在其他系統上執行,且沒有相應的庫時,解決辦法就是使用靜態庫。而且由於動態庫是在程式執行的時候被連結,因此動態庫的執行速度比較慢。
好了,我們瞭解了關於動態庫和靜態庫的相關知識,那麼如何使用GCC生成靜態庫和動態庫呢?
我們參考了《LinuxC程式設計大全》上面的例子,來總結GCC下編譯靜態及其動態連結庫的方法及步驟。
程式清單如下:
test.c
int add(int a,int b)
{
retrun a+b;
}
int sub(int a,int b)
{
retrun a-b;
}
int mul(int a,int b)
{
retrun a*b;
}
int div(int a,int b)
{
retrun a/b;
}
test.h的內容
#ifndef _TEST_H_
#define _TEST_H_
extern int add(int a,int b);
extern int sub(int a,int b);
extern int mul(int a,int b);
extern int div(int a,int b);
main.c檔案內容
#include<stduo.h>
#include<test.h>
int main()
{
int a,b;
printf("please input a and b\n");
scanf("%d %d",&a,&b);
printf("The add:%d\n",add(a,b));
printf("The sub:%d\n",sub(a,b));
printf("The mul:%d\n",mul(a,b));
printf("The div:%d\n",div(a,b));
}
1.使用gcc生成靜態庫及靜態庫使用方法:
在此例中,test.c用於編譯生成靜態庫libtest.a,test.h為libtest.a對應的標頭檔案。
第一步:生成test.o目標檔案,使用gcc -c test.c -o test.o命令。
第二步:使用ar將test.o打包成libtest.a靜態庫,使用ar rcs -o libtest.a test.o命令
第三步:生成libtest.a靜態庫後,可以使用命令ar t libtest.a檢視libtest.a檔案中包含哪些檔案。
第四步:編譯main.c,並使用libtest.a靜態庫,連結時-l引數後不加空格指定所需要連結的庫,這裡庫名是libtest.a,但是隻需要給出-ltest即可,ld會以libtest作為庫的實際名字。完整的命令為:gcc -o app_static main.c -L. -ltest 或者是gcc -o app_static main.c libtest.a
第五步:執行app_static
直接使用命令./app_static
2.使用gcc生成動態庫及使用動態庫的方法
第一步:生成test.o目標檔案,使用如下命令。在此處需要新增-fPIC引數,該引數用於生成位置無關程式碼已工生成動態庫使用,使用命令:gcc -c -o test.o -fPIC test.c
第二步:使用-shared引數生成動態庫,使用如下命令:gcc -shared -o libmyshare.so test.o,上述兩個命令可以連在一起,如下所示:gcc -shared -fPIC -o libmyshare.so test.c
第三步:編譯main.c,使用libmyshare.so動態庫,命令如下gcc -o app_share main.c -L. -lmyshare.使用ldd app_share命令檢視app_share使用動態庫,如果libmyshare無法找到,直接執行app_share就會出現錯誤。解決方法:首先使用export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH將當前目錄加入LD_LIBRARY_PATH變數中。再次執行ldd app_share
另一種編譯main.c,並連結libmyshare.so的方式如下(該方式通過./libmyshare.so直接指定使用當前目錄下的libmyshare.so檔案),使用命令:gcc -o app_share main.c ./libmyshare.so
來源 :http://www.cnblogs.com/skynet/p/3372855.html
1. 庫的介紹
庫是寫好的現有的,成熟的,可以複用的程式碼。現實中每個程式都要依賴很多基礎的底層庫,不可能每個人的程式碼都從零開始,因此庫的存在意義非同尋常。
本質上來說庫是一種可執行程式碼的二進位制形式,可以被作業系統載入記憶體執行。庫有兩種:靜態庫(.a、.lib)和動態庫(.so、.dll)。 windows上對應的是.lib .dll linux上對應的是.a .so
在這裡先介紹下Linux下的gcc編譯的幾個選項
g++ -c hellospeak.cpp
會將hellospeak.cpp 選項 -c 用來告訴編譯器編譯原始碼但不要執行連結,輸出結果為物件檔案。檔案預設名與原始碼檔名相同,只是將其後綴變為 .o。例如,上面的命令將編譯原始碼檔案hellospeak.cpp 並生成物件檔案 hellospeak.o;
下面這條命令將上述兩個原始碼檔案編譯連結成一個單一的可執行程式:
$ g++ hellospeak.cpp speak.cpp -o hellospeak
如果沒有-o和後面的引數,編譯器採用預設的 a.out
本例中就會生成hellospeak 這樣的可執行程式。
所謂靜態、動態是指連結。回顧一下,將一個程式編譯成可執行程式的步驟:
圖:編譯過程
靜態庫
之所以成為【靜態庫】,是因為在連結階段,會將彙編生成的目標檔案.o與引用到的庫一起連結打包到可執行檔案中。因此對應的連結方式稱為靜態連結。
試想一下,靜態庫與彙編生成的目標檔案一起連結為可執行檔案,那麼靜態庫必定跟.o檔案格式相似。其實一個靜態庫可以簡單看成是一組目標檔案(.o/.obj檔案)的集合,即很多目標檔案經過壓縮打包後形成的一個檔案。靜態庫特點總結:
l 靜態庫對函式庫的連結是放在編譯時期完成的。
l 程式在執行時與函式庫再無瓜葛,移植方便。
l 浪費空間和資源,因為所有相關的目標檔案與牽涉到的函式庫被連結合成一個可執行檔案。
Linux下建立與使用靜態庫
Linux靜態庫命名規則
Linux靜態庫命名規範,必須是"lib[your_library_name].a":lib為字首,中間是靜態庫名,副檔名為.a。
建立靜態庫(.a)
通過上面的流程可以知道,Linux建立靜態庫過程如下:
l 首先,將程式碼檔案編譯成目標檔案.o(StaticMath.o)
g++ -c StaticMath.cpp
注意帶引數-c,否則直接編譯為可執行檔案
然後,通過ar工具將目標檔案打包成.a靜態庫檔案
ar -crv libstaticmath.a StaticMath.o
生成靜態庫libstaticmath.a。
動態庫
通過上面的介紹發現靜態庫,容易使用和理解,也達到了程式碼複用的目的,那為什麼還需要動態庫呢?
為什麼還需要動態庫?
為什麼需要動態庫,其實也是靜態庫的特點導致。
l 空間浪費是靜態庫的一個問題。
另一個問題是靜態庫對程式的更新、部署和釋出頁會帶來麻煩。如果靜態庫liba.lib更新了,所以使用它的應用程式都需要重新編譯、釋出給使用者(對於玩家來說,可能是一個很小的改動,卻導致整個程式重新下載,全量更新)。
動態庫在程式編譯時並不會被連線到目的碼中,而是在程式執行是才被載入。不同的應用程式如果呼叫相同的庫,那麼在記憶體裡只需要有一份該共享庫的例項,規避了空間浪費問題。動態庫在程式執行是才被載入,也解決了靜態庫對程式的更新、部署和釋出頁會帶來麻煩。使用者只需要更新動態庫即可,增量更新。
動態庫特點總結:
l 動態庫把對一些庫函式的連結載入推遲到程式執行的時期。
l 可以實現程序之間的資源共享。(因此動態庫也稱為共享庫)
l 將一些程序升級變得簡單。
l 甚至可以真正做到連結載入完全由程式設計師在程式程式碼中控制(顯示呼叫)。
Window與Linux執行檔案格式不同,在建立動態庫的時候有一些差異。
l 在Windows系統下的執行檔案格式是PE格式,動態庫需要一個DllMain函式做出初始化的入口,通常在匯出函式的宣告時需要有_declspec(dllexport)關鍵字。
l Linux下gcc編譯的執行檔案預設是ELF格式,不需要初始化入口,亦不需要函式做特別的宣告,編寫比較方便。
與建立靜態庫不同的是,不需要打包工具(ar、lib.exe),直接使用編譯器即可建立動態庫。