鹽城網(wǎng)站建設(shè)報價廣州市疫情最新
目錄
前言
一、程序的翻譯過程
1、預(yù)編譯
2、編譯
3、匯編
4、鏈接
(1)鏈接做了什么
(2)動態(tài)鏈接
(3)靜態(tài)鏈接
(4)如何使用gcc進(jìn)行動態(tài)鏈接和靜態(tài)鏈接
前言
????????本章主要帶著大家一起學(xué)習(xí)Linux下編譯C/C++的工具,以及關(guān)于動靜態(tài)庫的一些基礎(chǔ)知識;
一、程序的翻譯過程
? ? ? ? 我們的C語言和C++編寫出的程序?qū)儆诜g型程序,此處以C語言為例,分析程序翻譯過程;
1、預(yù)編譯
????????當(dāng)我們寫完一個C語言程序時,首先,我們需要進(jìn)行預(yù)編譯操作;預(yù)編譯主要將頭文件展開、宏替換、條件編譯與去注釋;我們可使用下列指令生成我們的預(yù)編譯完的文件(以.i為結(jié)尾的文件后綴);
gcc -o?生成文件名.i? -E 被預(yù)編譯的文件名.c
????????上圖為我們編寫的源程序,我們來驗證我們的預(yù)編譯階段是否完成了上述工作;我們執(zhí)行以下執(zhí)行,生成預(yù)編譯完成后的文件 test.i ;
? ? ? ? 預(yù)編譯生成的文件從大小上,明顯可以看出比我們的源文件要大很多,我們再使用vim查看我們的test.i文件,如下圖;
????????我們發(fā)現(xiàn)我們的文件一下就變成了八百多行,前面增加的便是我們的stdio文件展開后的樣子,故我們test.i文件變大了很多,因為把我們的庫文件代碼復(fù)制到預(yù)編譯后的文件中了,而且我們不難發(fā)現(xiàn)我們之前些的注釋被去掉了,我們的宏也完成了替換,也進(jìn)行了條件編譯的處理,與我們的預(yù)想完全一致;
2、編譯
????????這一步主要實現(xiàn)的將我們的C語言代碼編譯成匯編代碼,我們生成的匯編代碼文件以s為后綴名,具體指令如下;
gcc -o 編譯生成后文件名.s? -S? .c文件或.i文件都可
????????學(xué)過匯編語言的友友們,應(yīng)該就很熟悉了,這就是我們的匯編代碼,編譯階段也如我們所料完成了自己的工作;
3、匯編
????????這一步主要是將我們的匯編代碼轉(zhuǎn)換成機(jī)器指令,生成的文件叫?可重定向二進(jìn)制目標(biāo)文件(目標(biāo)文件),這個文件在Linux下一般以o為后綴名,在window下一般以obj為后綴名,具體指令如下;
gcc -o 生成文件名?-c .c文件或.i文件或.s文件都可?
????????我們可以使用od指令來閱讀我們的可重定向目標(biāo)文件,如下所示;
????????這里生成的已經(jīng)是機(jī)器指令了,但是仍然不能直接執(zhí)行,必須鏈接后才可以執(zhí)行;
4、鏈接
(1)鏈接做了什么
????????實際上,我們上面的頭文件,如 stdio.h 只有函數(shù)的聲明,并沒有函數(shù)的定義,因此我們在是使用我們庫函數(shù)printf、scanf函數(shù)時,光有函數(shù)聲明時遠(yuǎn)遠(yuǎn)不夠的,我們必須還有具體的函數(shù)定義,這些函數(shù)定義都放在動態(tài)庫或靜態(tài)庫,因此我們鏈接也有動態(tài)鏈接和靜態(tài)鏈接;我們首先完成鏈接,具體指令如下;
gcc -o 生成可執(zhí)行程序名? .c文件/.i文件/.s文件/.o文件都可
????????我們可以通過ldd指令或file指令查看文件使用的動態(tài)庫/靜態(tài)庫的名字,以及采用何種鏈接方式;
補(bǔ)充:在Linux下,動態(tài)庫文件的后綴為.so,靜態(tài)庫文件的后綴為.a,而在window下,動態(tài)庫文件的后綴為.dll,靜態(tài)庫文件的后綴為.lib;
(2)動態(tài)鏈接
????????在動態(tài)鏈接中,我們的程序一旦發(fā)現(xiàn)有需要使用庫函數(shù)的代碼,則會保存該庫函數(shù)在動態(tài)庫中的地址,運(yùn)行到此部分時,我們直接通過地址跳轉(zhuǎn)到動態(tài)庫的代碼中,執(zhí)行函數(shù),執(zhí)行完函數(shù)后繼續(xù)返回程序執(zhí)行程序后續(xù)代碼;因此,一旦我們的動態(tài)庫出現(xiàn)了問題,我們的程序就無法正確執(zhí)行;
總結(jié):
動態(tài)鏈接優(yōu)點:可執(zhí)行程序較小,因為只保存了庫函數(shù)地址;
動態(tài)鏈接缺點:十分依賴動態(tài)庫文件,若文件出現(xiàn)問題,程序則可能出現(xiàn)問題;
(3)靜態(tài)鏈接
? ? ? ? 在靜態(tài)鏈接中,我們的程序會將需要使用的庫函數(shù)的定義直接拷貝一份到我們的可執(zhí)行程序中,我們在運(yùn)行可執(zhí)行程序時,無需靜態(tài)庫,我們直接調(diào)自己拷貝的代碼即可;
總結(jié):
靜態(tài)鏈接優(yōu)點:對靜態(tài)庫文件的無依賴,一旦生成可執(zhí)行程序,即使靜態(tài)庫被刪除,也可以運(yùn)行;
靜態(tài)鏈接缺點:靜態(tài)鏈接生成的可執(zhí)行程序由于會拷貝靜態(tài)庫中的函數(shù)定義,因此會變得非常大;
(4)如何使用gcc進(jìn)行動態(tài)鏈接和靜態(tài)鏈接
????????在gcc中,我們默認(rèn)使用動態(tài)鏈接的方式進(jìn)行鏈接,因此直接使用正常的方式生成可執(zhí)行程序即可,例如;
gcc -o test-d test.c
? ? ? ? 而我們?nèi)粝胧褂渺o態(tài)鏈接的方式進(jìn)行連接,我們加上一個static選項即可,如下所示;
gcc -o test-s test.c -static
補(bǔ)充:有些小伙伴可能沒有安裝靜態(tài)庫,可通過下面指令安裝靜態(tài)庫
sudo yum install -y glibc-static (Centos)
? ? ? ? 顯然,我們使用靜態(tài)鏈接生成的可執(zhí)行程序明顯比動態(tài)生成的可執(zhí)行程序要大十倍左右,也符合我們之前的推理,我們在通過ldd和file指令查看我們生成的可執(zhí)行程序所依賴的庫;
????????test-s是靜態(tài)鏈接生成的可執(zhí)行程序,因此沒有依賴的動態(tài)庫,而test-d是動態(tài)鏈接生成的可執(zhí)行程序,因此有依賴的動態(tài)庫,我們可以通過file看出這兩個可執(zhí)行程序是動態(tài)鏈接還是靜態(tài)鏈接;