1. 程式人生 > >Linux程式設計(一)——靜態庫

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.

共享庫和靜態庫的區別

靜態庫有一個缺點,當你同時執行多個程式並且他們都使用同一個函式庫的函式時,記憶體就會有多個函式的副本,這會浪費大量的記憶體資源。而共享庫可以解決這種問題。

當一個程式使用共享庫時,它的連結方式是這樣的:程式本身不包含函式程式碼,而是引用執行時可訪問的共享程式碼。當編譯好的程式被裝載到記憶體執行時,函式引用被解析併產生對共享庫的呼叫,如果有必要,共享庫才會被載入到記憶體中。