Linux程式設計(一)——靜態庫
靜態庫概述(度娘一波)
靜態庫,也稱歸檔檔案(archive),是指在我們的應用中,有一些公共程式碼是需要反覆使用,就把這些程式碼編譯為“庫”檔案;在連結步驟中,聯結器將從庫檔案取得所需的程式碼,複製到生成的可執行檔案中的這種庫。
程式編譯一般需經預處理、編譯、彙編和連結幾個步驟。靜態庫特點是可執行檔案中包含了庫程式碼的一份完整拷貝;缺點就是被多次使用就會有多份冗餘拷貝。
靜態庫(*.a)(這裡的.a指的是)和動態庫(*.)是兩種共享程式程式碼的方式,它們的區別是:靜態庫在程式的連結階段被複制到了程式中,和程式執行的時候沒有關係;動態庫在連結階段沒有被複制到程式中,而是程式在執行時由系統動態載入到記憶體中供程式呼叫。使用動態庫的優點是系統只需載入一次動態庫,不同的程式可以得到記憶體中相同的動態庫的副本,因此節省了很多記憶體。
建立和使用靜態庫
編譯過程一覽
預處理 .i .ii檔案 gcc -E hello.cpp -o hello.i
編譯 .s檔案 gcc -S hello.cpp -o hello.s
彙編 .o .obj檔案 gcc -c hello.cpp -o hello.o
連結 .lib .a檔案
假設我們現在有兩個檔案,main.cpp和func.cpp,main.cpp使用了func.cpp裡面的函式,那麼在彙編完成產生目標檔案後,連結器會在連結時將兩個檔案連結成可執行檔案(.out)檔案。下面是例子
//main.c #include<stdlib.h> #include"lib.h" //標頭檔案 int main() { func("Hello World"); //宣告在lib.h標頭檔案裡 exit(0); }
//標頭檔案 lib.h
void func(char* );
//func.c
#include<stdio.h>
void func(char* a)
{
printf("%s\n",a);
}
過程:
1. 使用gcc -c 生成目標檔案
gcc -c main.c func.c
執行完會生成main.o和func.o目標檔案
2. 接下來要連結成可執行檔案
gcc -o proexec main.o func.o
這條命令會生成一個名為proexec的可執行檔案,直接執行就可以看到輸出“hello world”。
上面編譯過程特地將連結過程單獨分出來,是因為我們在使用靜態庫的時候,其實是在連結的時候和目標檔案一起生成可執行檔案的過程。假設我們又有一個檔案,裡面有其他我們沒用上的函式
//otherfunc.c
#include<stdlio.h>
void otherfunc(int arg)
{
printf("other func\n");
}
我們不希望使用其他函式的時候還得一個一個寫命令,希望把他們打包到一個靜態庫檔案裡,後面使用到任何在這個庫裡面到函式,只需要加入這個庫的路徑就可以連結成可執行檔案了,而不用gcc -o x.o y.o z.o這麼麻煩。我們需要如下命令:
gcc -c other.c //編譯成目標檔案
ar crv lib.a other.o func.o //打包成靜態庫
執行完後,會生成靜態庫檔案。
gcc -o proexec2 main.o lib.a
這時候就不用一個一個函式指定了,直接連結靜態庫,就會生成我們需要的可執行檔案proexec2.
共享庫和靜態庫的區別
靜態庫有一個缺點,當你同時執行多個程式並且他們都使用同一個函式庫的函式時,記憶體就會有多個函式的副本,這會浪費大量的記憶體資源。而共享庫可以解決這種問題。
當一個程式使用共享庫時,它的連結方式是這樣的:程式本身不包含函式程式碼,而是引用執行時可訪問的共享程式碼。當編譯好的程式被裝載到記憶體執行時,函式引用被解析併產生對共享庫的呼叫,如果有必要,共享庫才會被載入到記憶體中。