GCC编译过程与动态链接库和静态链接库
1. 庫(kù)的介紹
庫(kù)是寫好的現(xiàn)有的,成熟的,可以復(fù)用的代碼。現(xiàn)實(shí)中每個(gè)程序都要依賴很多基礎(chǔ)的底層庫(kù),不可能每個(gè)人的代碼都從零開(kāi)始,因此庫(kù)的存在意義非同尋常。
本質(zhì)上來(lái)說(shuō)庫(kù)是一種可執(zhí)行代碼的二進(jìn)制形式,可以被操作系統(tǒng)載入內(nèi)存執(zhí)行。庫(kù)有兩種:靜態(tài)庫(kù)(.a、.lib)和動(dòng)態(tài)庫(kù)(.so、.dll)。 windows上對(duì)應(yīng)的是.lib .dll linux上對(duì)應(yīng)的是.a .so
在這里先介紹下Linux下的gcc編譯的幾個(gè)選項(xiàng)
g++ -c hellospeak.cpp 會(huì)將hellospeak.cpp 選項(xiàng) -c 用來(lái)告訴編譯器編譯源代碼但不要執(zhí)行鏈接,輸出結(jié)果為對(duì)象文件。文件默認(rèn)名與源碼文件名相同,只是將其后綴變?yōu)?.o。例如,上面的命令將編譯源碼文件hellospeak.cpp 并生成對(duì)象文件 hellospeak.o;
下面這條命令將上述兩個(gè)源碼文件編譯鏈接成一個(gè)單一的可執(zhí)行程序:?
$ g++ hellospeak.cpp speak.cpp -o hellospeak 如果沒(méi)有-o和后面的參數(shù),編譯器采用默認(rèn)的 a.out
本例中就會(huì)生成hellospeak 這樣的可執(zhí)行程序。
所謂靜態(tài)、動(dòng)態(tài)是指鏈接。回顧一下,將一個(gè)程序編譯成可執(zhí)行程序的步驟:
圖:編譯過(guò)程
靜態(tài)庫(kù)之所以成為【靜態(tài)庫(kù)】,是因?yàn)樵阪溄与A段,會(huì)將匯編生成的目標(biāo)文件.o與引用到的庫(kù)一起鏈接打包到可執(zhí)行文件中。因此對(duì)應(yīng)的鏈接方式稱為靜態(tài)鏈接。
試想一下,靜態(tài)庫(kù)與匯編生成的目標(biāo)文件一起鏈接為可執(zhí)行文件,那么靜態(tài)庫(kù)必定跟.o文件格式相似。其實(shí)一個(gè)靜態(tài)庫(kù)可以簡(jiǎn)單看成是一組目標(biāo)文件(.o/.obj文件)的集合,即很多目標(biāo)文件經(jīng)過(guò)壓縮打包后形成的一個(gè)文件。靜態(tài)庫(kù)特點(diǎn)總結(jié):
l 靜態(tài)庫(kù)對(duì)函數(shù)庫(kù)的鏈接是放在編譯時(shí)期完成的。
l 程序在運(yùn)行時(shí)與函數(shù)庫(kù)再無(wú)瓜葛,移植方便。
l 浪費(fèi)空間和資源,因?yàn)樗邢嚓P(guān)的目標(biāo)文件與牽涉到的函數(shù)庫(kù)被鏈接合成一個(gè)可執(zhí)行文件。
Linux下創(chuàng)建與使用靜態(tài)庫(kù)Linux靜態(tài)庫(kù)命名規(guī)則
Linux靜態(tài)庫(kù)命名規(guī)范,必須是"lib[your_library_name].a":lib為前綴,中間是靜態(tài)庫(kù)名,擴(kuò)展名為.a。
創(chuàng)建靜態(tài)庫(kù)(.a)通過(guò)上面的流程可以知道,Linux創(chuàng)建靜態(tài)庫(kù)過(guò)程如下:
l 首先,將代碼文件編譯成目標(biāo)文件.o(StaticMath.o)
g++ -c StaticMath.cpp注意帶參數(shù)-c,否則直接編譯為可執(zhí)行文件
然后,通過(guò)ar工具將目標(biāo)文件打包成.a靜態(tài)庫(kù)文件
ar -crv libstaticmath.a StaticMath.o生成靜態(tài)庫(kù)libstaticmath.a。
?
動(dòng)態(tài)庫(kù)通過(guò)上面的介紹發(fā)現(xiàn)靜態(tài)庫(kù),容易使用和理解,也達(dá)到了代碼復(fù)用的目的,那為什么還需要?jiǎng)討B(tài)庫(kù)呢?
為什么還需要?jiǎng)討B(tài)庫(kù)?為什么需要?jiǎng)討B(tài)庫(kù),其實(shí)也是靜態(tài)庫(kù)的特點(diǎn)導(dǎo)致。
l 空間浪費(fèi)是靜態(tài)庫(kù)的一個(gè)問(wèn)題。
另一個(gè)問(wèn)題是靜態(tài)庫(kù)對(duì)程序的更新、部署和發(fā)布頁(yè)會(huì)帶來(lái)麻煩。如果靜態(tài)庫(kù)liba.lib更新了,所以使用它的應(yīng)用程序都需要重新編譯、發(fā)布給用戶(對(duì)于玩家來(lái)說(shuō),可能是一個(gè)很小的改動(dòng),卻導(dǎo)致整個(gè)程序重新下載,全量更新)。
動(dòng)態(tài)庫(kù)在程序編譯時(shí)并不會(huì)被連接到目標(biāo)代碼中,而是在程序運(yùn)行是才被載入。不同的應(yīng)用程序如果調(diào)用相同的庫(kù),那么在內(nèi)存里只需要有一份該共享庫(kù)的實(shí)例,規(guī)避了空間浪費(fèi)問(wèn)題。動(dòng)態(tài)庫(kù)在程序運(yùn)行是才被載入,也解決了靜態(tài)庫(kù)對(duì)程序的更新、部署和發(fā)布頁(yè)會(huì)帶來(lái)麻煩。用戶只需要更新動(dòng)態(tài)庫(kù)即可,增量更新。
動(dòng)態(tài)庫(kù)特點(diǎn)總結(jié):
l 動(dòng)態(tài)庫(kù)把對(duì)一些庫(kù)函數(shù)的鏈接載入推遲到程序運(yùn)行的時(shí)期。
l 可以實(shí)現(xiàn)進(jìn)程之間的資源共享。(因此動(dòng)態(tài)庫(kù)也稱為共享庫(kù))
l 將一些程序升級(jí)變得簡(jiǎn)單。
l 甚至可以真正做到鏈接載入完全由程序員在程序代碼中控制(顯示調(diào)用)。
Window與Linux執(zhí)行文件格式不同,在創(chuàng)建動(dòng)態(tài)庫(kù)的時(shí)候有一些差異。
l 在Windows系統(tǒng)下的執(zhí)行文件格式是PE格式,動(dòng)態(tài)庫(kù)需要一個(gè)DllMain函數(shù)做出初始化的入口,通常在導(dǎo)出函數(shù)的聲明時(shí)需要有_declspec(dllexport)關(guān)鍵字。
l Linux下gcc編譯的執(zhí)行文件默認(rèn)是ELF格式,不需要初始化入口,亦不需要函數(shù)做特別的聲明,編寫比較方便。
與創(chuàng)建靜態(tài)庫(kù)不同的是,不需要打包工具(ar、lib.exe),直接使用編譯器即可創(chuàng)建動(dòng)態(tài)庫(kù)。
參考于:
http://www.cnblogs.com/skynet/p/3372855.html 吳秦 (很詳細(xì)!值得細(xì)看) http://www.cnblogs.com/iloveyoucc/archive/2012/08/29/2661851.html 參考來(lái)源:知乎?郭無(wú)心2.? GCC編譯過(guò)程 2.1 GCC定義 目前 Linux 下最常用的 C 語(yǔ)言編譯器是 GCC ( GNU Compiler Collection ),它是 GNU 項(xiàng)目中符合 ANSI C 標(biāo)準(zhǔn)的編譯系統(tǒng),能夠編譯用 C 、 C++ 和 Object C 等語(yǔ)言編寫的程序。 GCC 不僅功能非常強(qiáng)大,結(jié)構(gòu)也異常靈活。最值得稱道的一點(diǎn)就是它可以通過(guò)不同的前端模塊來(lái)支持各種語(yǔ)言,如Java 、 Fortran 、 Pascal 、 Modula-3 和 Ada 等。開(kāi)放、自由和靈活是 Linux 的魅力所在,而這一點(diǎn)在 GCC 上的體現(xiàn)就是程序員通過(guò)它能夠更好地控制整個(gè)編譯過(guò)程。在使用 GCC 編譯程序時(shí),編譯過(guò)程可以被細(xì)分為四個(gè)階段: 預(yù)處理( Pre-Processing )
編譯( Compiling )
匯編( Asse mbling )
鏈接( Linking )
Linux 程序員可以根據(jù)自己的需要讓 GCC 在編譯的任何階段結(jié)束,以便檢查或使用編譯器在該階段的輸出信息,或者對(duì)最后生成的二進(jìn)制文件進(jìn)行控制,以便通過(guò)加入不同數(shù)量和種類的調(diào)試代碼來(lái)為今后的調(diào)試做好準(zhǔn)備。和其它常用的編譯器一樣, GCC 也提供了靈活而強(qiáng)大的代碼優(yōu)化功能,利用它可以生成執(zhí)行效率更高的代碼。
GCC 提供了 30 多條警告信息和三個(gè)警告級(jí)別,使用它們有助于增強(qiáng)程序的穩(wěn)定性和可移植性。此外, GCC 還對(duì)標(biāo)準(zhǔn)的 C 和 C++ 語(yǔ)言進(jìn)行了大量的擴(kuò)展,提高程序的執(zhí)行效率,有助于編譯器進(jìn)行代碼優(yōu)化,能夠減輕編程的工作量。 ???????
?
2.2 GCC編譯過(guò)程
1)gcc 預(yù)處理階段:主要對(duì)包含的頭文件(#include )和宏定義(#define,#ifdef … )進(jìn)行處理。可以使用“gcc -E” 讓gcc 在預(yù)處理之后停止編譯過(guò)程,生成 *.i 文件。
gcc -E hello.c -o hello.i2)gcc 編譯階段:gcc 首先要檢查代碼的規(guī)范性,是否有語(yǔ)法錯(cuò)誤等。以確定代碼實(shí)際要做的工作,在檢查無(wú)誤后,gcc 把代碼翻譯成匯編語(yǔ)言。用戶可以使用-S 選項(xiàng)進(jìn)行查看,該選項(xiàng)只進(jìn)
行編譯而不進(jìn)行匯編,生成匯編代碼。
3)gcc 匯編階段:生成目標(biāo)代碼 *.o ;有兩種方式:使用 gcc 直接從源代碼生成目標(biāo)代碼 gcc -c *.s -o *.o 以及使用匯編器從匯編代碼生成目標(biāo)代碼 as *.s -o *.o
gcc -c hello.s -o hello.o as hello.s -o hello.o也可以直接使用as *.s, 將執(zhí)行匯編、鏈接過(guò)程生成可執(zhí)行文件a.out, 可以像上面使用-o 選項(xiàng)指定輸出文件的格式。
4)gcc 鏈接階段:生成可執(zhí)行文件;可以生成的可執(zhí)行文件格式有: a.out/*/,當(dāng)然可能還有其它格式。
2.3 gcc 常用編譯選項(xiàng):
?
?
2.3? gcc 鏈接庫(kù)文件的使用?????? 在 linux 下開(kāi)發(fā)軟件時(shí),完全不使用第三方函數(shù)庫(kù)的情況是比較少見(jiàn)的,通常來(lái)講都需要借助一個(gè)或多個(gè)函數(shù)庫(kù)的支持才能夠完成相應(yīng)的功能。從程序員的角度看,函數(shù)庫(kù)實(shí)際上就是一些頭文件( .h )和庫(kù)文件( .so 或者 .a )的集合。雖然 Linux 下的大多數(shù)函數(shù)都默認(rèn)將頭文件放到/usr/include/?目錄下,而庫(kù)文件則放到 /usr/lib/?目錄下,但并不是所有的情況都是這樣。正因如此, GCC 在編譯時(shí)必須有自己的辦法來(lái)查找所需要的頭文件和庫(kù)文件。 GCC 采用搜索目錄的辦法來(lái)查找所需要的文件, -I 選項(xiàng)可以向 GCC 的頭文件搜索路徑中添加新的目錄。例如,如果在/home/justin/include/ 目錄下有編譯時(shí)所需要的頭文件,為了讓 GCC 能夠順利地找到它們,就可以使用 -I 選項(xiàng):
gcc foo.c -I /home/justin/include -o foo????? 同樣,如果使用了不在標(biāo)準(zhǔn)位置的庫(kù)文件,那么可以通過(guò) -L 選項(xiàng)向 GCC 的庫(kù)文件搜索路徑中添加新的目錄。例如,如果在 /home/xiaowp/lib/ 目錄下有鏈接時(shí)所需要的庫(kù)文件 libfoo.so ,為了讓 GCC 能夠順利地找到它,可以使用下面的命令:
gcc foo.c -L /home/justin/lib -lfoo -o foo???? 值得好好解釋一下的是?-l 選項(xiàng),它指示 GCC 去連接庫(kù)文件 libfoo.so 。 Linux 下的庫(kù)文件在命名時(shí)有一個(gè)約定,那就是應(yīng)該以lib 三個(gè)字母開(kāi)頭,由于所有的庫(kù)文件都遵循了同樣的規(guī)范,因此在用-l 選項(xiàng)指定鏈接的庫(kù)文件名時(shí)可以省去lib 三個(gè)字母,也就是說(shuō)GCC 在對(duì)-lfoo 進(jìn)行處理時(shí),會(huì)自動(dòng)去鏈接名為libfoo.so 。
Linux 下的庫(kù)文件分為兩大類分別是動(dòng)態(tài)鏈接庫(kù)(通常以.so 結(jié)尾)和靜態(tài)鏈接庫(kù)(通常以.a 結(jié)尾),兩者的差別僅在程序執(zhí)行時(shí)所需的代碼是在運(yùn)行時(shí)動(dòng)態(tài)加載的,還是在編譯時(shí)靜態(tài)加載的 。默認(rèn)情況下,GCC 在鏈接時(shí)優(yōu)先使用動(dòng)態(tài)鏈接庫(kù),只有當(dāng)動(dòng)態(tài)鏈接庫(kù)不存在時(shí)才考慮使用靜態(tài)鏈接庫(kù),如果需要的話可以在編譯時(shí)加上-static?選項(xiàng),強(qiáng)制使用靜態(tài)鏈接庫(kù)。例如,如果在home/justin/lib/ 目錄下有鏈接時(shí)所需要的庫(kù)文件libfoo.so 和libfoo.a ,為了讓GCC 在鏈接時(shí)只用到靜態(tài)鏈接庫(kù),可以使用下面的命令:
?對(duì)于動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)文件的創(chuàng)建方法,見(jiàn)上文庫(kù)的介紹。
轉(zhuǎn)載于:https://www.cnblogs.com/fengliu-/p/10216852.html
總結(jié)
以上是生活随笔為你收集整理的GCC编译过程与动态链接库和静态链接库的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 现已加入肯德基豪华午餐什么意思
- 下一篇: 三星:车规级 GDDR7 专为超高带宽计