庫是寫好的現有的,成熟的,可以復用的代碼。現實中每個程序都要依賴很多基礎的底層庫,不可能每個人的代碼都從零開始,因此庫的存在意義非同尋常。
本質上來說庫是一種可執行代碼的二進制形式,可以被操作系統載入內存執行。庫有兩種:靜態庫(.a、.lib)和動態庫(.so、.dll)。 windows上對應的是.lib .dll linux上對應的是.a .so
在這里先介紹下Linux下的gcc編譯的幾個選項
g++ -c hellospeak.cpp
下面這條命令將上述兩個源碼文件編譯鏈接成一個單一的可執行程序:
$ g++ hellospeak.cpp speak.cpp -o hellospeak
本例中就會生成hellospeak 這樣的可執行程序。
所謂靜態、動態是指鏈接。回顧一下,將一個程序編譯成可執行程序的步驟:
圖:編譯過程

之所以成為【靜態庫】,是因為在鏈接階段,會將匯編生成的目標文件.o與引用到的庫一起鏈接打包到可執行文件中。因此對應的鏈接方式稱為靜態鏈接。
試想一下,靜態庫與匯編生成的目標文件一起鏈接為可執行文件,那么靜態庫必定跟.o文件格式相似。其實一個靜態庫可以簡單看成是一組目標文件(.o/.obj文件)的集合,即很多目標文件經過壓縮打包后形成的一個文件。靜態庫特點總結:
l 靜態庫對函數庫的鏈接是放在編譯時期完成的。
l 程序在運行時與函數庫再無瓜葛,移植方便。
l 浪費空間和資源,因為所有相關的目標文件與牽涉到的函數庫被鏈接合成一個可執行文件。
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 空間浪費是靜態庫的一個問題。
![clip_image021[4] clip_image021[4]](/image/aHR0cHM6Ly9pbWFnZXMwLmNuYmxvZ3MuY29tL2Jsb2cvOTIwNzEvMjAxMzEwLzE2MjAxNjEzLTU4NzdmNjc0MDQ4ZjQ1ZDU4ZmU5YmI0MDIyNGQzYjRkLnBuZw==.png)
另一個問題是靜態庫對程序的更新、部署和發布頁會帶來麻煩。如果靜態庫liba.lib更新了,所以使用它的應用程序都需要重新編譯、發布給用戶(對於玩家來說,可能是一個很小的改動,卻導致整個程序重新下載,全量更新)。
動態庫在程序編譯時並不會被連接到目標代碼中,而是在程序運行是才被載入。不同的應用程序如果調用相同的庫,那么在內存里只需要有一份該共享庫的實例,規避了空間浪費問題。動態庫在程序運行是才被載入,也解決了靜態庫對程序的更新、部署和發布頁會帶來麻煩。用戶只需要更新動態庫即可,增量更新。

動態庫特點總結:
l 動態庫把對一些庫函數的鏈接載入推遲到程序運行的時期。
l 可以實現進程之間的資源共享。(因此動態庫也稱為共享庫)
l 將一些程序升級變得簡單。
l 甚至可以真正做到鏈接載入完全由程序員在程序代碼中控制(顯示調用)。
Window與Linux執行文件格式不同,在創建動態庫的時候有一些差異。
l 在Windows系統下的執行文件格式是PE格式,動態庫需要一個DllMain函數做出初始化的入口,通常在導出函數的聲明時需要有_declspec(dllexport)關鍵字。
l Linux下gcc編譯的執行文件默認是ELF格式,不需要初始化入口,亦不需要函數做特別的聲明,編寫比較方便。
與創建靜態庫不同的是,不需要打包工具(ar、lib.exe),直接使用編譯器即可創建動態庫。
參考於:
http://www.cnblogs.com/skynet/p/3372855.html
吳秦
(很詳細!值得細看)
編譯( Compiling )
匯編( Asse mbling )
鏈接( Linking )
Linux 程序員可以根據自己的需要讓 GCC 在編譯的任何階段結束,以便檢查或使用編譯器在該階段的輸出信息,或者對最后生成的二進制文件進行控制,以便通過加入不同數量和種類的調試代碼來為今后的調試做好准備。和其它常用的編譯器一樣, GCC 也提供了靈活而強大的代碼優化功能,利用它可以生成執行效率更高的代碼。
GCC 提供了 30 多條警告信息和三個警告級別,使用它們有助於增強程序的穩定性和可移植性。此外, GCC 還對標准的 C 和 C++ 語言進行了大量的擴展,提高程序的執行效率,有助於編譯器進行代碼優化,能夠減輕編程的工作量。


2.2 GCC編譯過程
1)gcc 預處理階段:主要對包含的頭文件(#include )和宏定義(#define,#ifdef … )進行處理。可以使用“gcc -E” 讓gcc 在預處理之后停止編譯過程,生成 *.i 文件。
gcc -E hello.c -o hello.i
2)gcc 編譯階段:gcc 首先要檢查代碼的規范性,是否有語法錯誤等。以確定代碼實際要做的工作,在檢查無誤后,gcc 把代碼翻譯成匯編語言。用戶可以使用-S 選項進行查看,該選項只進
行編譯而不進行匯編,生成匯編代碼。
gcc -S hello.i -o hello.s
3)gcc 匯編階段:生成目標代碼 *.o ;有兩種方式:使用 gcc 直接從源代碼生成目標代碼 gcc -c *.s -o *.o 以及使用匯編器從匯編代碼生成目標代碼 as *.s -o *.o
gcc -c hello.s -o hello.o
as hello.s -o hello.o
也可以直接使用as *.s, 將執行匯編、鏈接過程生成可執行文件a.out, 可以像上面使用-o 選項指定輸出文件的格式。
4)gcc 鏈接階段:生成可執行文件;可以生成的可執行文件格式有: a.out/*/,當然可能還有其它格式。
gcc hello.o 生成可執行文件 a.out
gcc hello.o -o hello 生成可執行文件 hello
2.3 gcc 常用編譯選項:
2.3 gcc 鏈接庫文件的使用
在 linux 下開發軟件時,完全不使用第三方函數庫的情況是比較少見的,通常來講都需要借助一個或多個函數庫的支持才能夠完成相應的功能。從程序員的角度看,函數庫實際上就是一些頭文件( .h )和庫文件( .so 或者 .a )的集合。雖然 Linux 下的大多數函數都默認將頭文件放到/usr/include/ 目錄下,而庫文件則放到 /usr/lib/ 目錄下,但並不是所有的情況都是這樣。正因如此, GCC 在編譯時必須有自己的辦法來查找所需要的頭文件和庫文件。 GCC 采用搜索目錄的辦法來查找所需要的文件, -I 選項可以向 GCC 的頭文件搜索路徑中添加新的目錄。例如,如果在/home/justin/include/ 目錄下有編譯時所需要的頭文件,為了讓 GCC 能夠順利地找到它們,就可以使用 -I 選項:
gcc foo.c -I /home/justin/include -o foo
同樣,如果使用了不在標准位置的庫文件,那么可以通過 -L 選項向 GCC 的庫文件搜索路徑中添加新的目錄。例如,如果在 /home/xiaowp/lib/ 目錄下有鏈接時所需要的庫文件 libfoo.so ,為了讓 GCC 能夠順利地找到它,可以使用下面的命令:
gcc foo.c -L /home/justin/lib -lfoo -o foo
值得好好解釋一下的是 -l 選項,它指示 GCC 去連接庫文件 libfoo.so 。 Linux 下的庫文件在命名時有一個約定,那就是應該以lib 三個字母開頭,由於所有的庫文件都遵循了同樣的規范,因此在用-l 選項指定鏈接的庫文件名時可以省去lib 三個字母,也就是說GCC 在對-lfoo 進行處理時,會自動去鏈接名為libfoo.so 。
Linux 下的庫文件分為兩大類分別是動態鏈接庫(通常以.so 結尾)和靜態鏈接庫(通常以.a 結尾),兩者的差別僅在程序執行時所需的代碼是在運行時動態加載的,還是在編譯時靜態加載的 。默認情況下,GCC 在鏈接時優先使用動態鏈接庫,只有當動態鏈接庫不存在時才考慮使用靜態鏈接庫,如果需要的話可以在編譯時加上-static 選項,強制使用靜態鏈接庫。例如,如果在home/justin/lib/ 目錄下有鏈接時所需要的庫文件libfoo.so 和libfoo.a ,為了讓GCC 在鏈接時只用到靜態鏈接庫,可以使用下面的命令:
gcc foo.c -L /home/justin/lib -static -lfoo -o foo
對於動態庫和靜態庫文件的創建方法,見上文庫的介紹。