目錄
I.Linux編譯器
1.gcc/g++編譯器
在C代碼生成可執(zhí)行程序的過程中,會有四個過程:
1預(yù)處理,2編譯,3匯編,4鏈接
Linux對.c文件分辨進(jìn)行預(yù)處理,編譯,匯編三大步指令:
預(yù)處理指令:
編譯指令:
匯編指令:
接下來說一說鏈接過程:
II.動靜態(tài)鏈接?
一.動態(tài)鏈接
二.靜態(tài)鏈接
三.兩個鏈接的區(qū)別:
III.動靜態(tài)庫
安裝C/C++靜態(tài)庫的指令:
file指令:
總結(jié):
I.Linux編譯器
1.gcc/g++編譯器
gcc是用來編譯C語言代碼的編譯器,而g++是用來編譯C++代碼的編譯器的。
而gcc和g++都是軟件,需要使用yum進(jìn)行下載
注:需要使用root權(quán)限才能下載?
在C代碼生成可執(zhí)行程序的過程中,會有四個過程:
1預(yù)處理,2編譯,3匯編,4鏈接
預(yù)處理:從test.c開始,該代碼文件會通過預(yù)處理后便停下來,形成test.i文件(頭文件展開,去注釋,宏替換,條件編譯)。
編譯:從test.i開始,該代碼文件會轉(zhuǎn)換成匯編語言,形成test.s文件。
匯編:從test.s開始,該代碼文件會轉(zhuǎn)換成計算機(jī)能讀懂的二進(jìn)制文件,形成test.o文件。
鏈接 :將多個test.o文件由鏈接起綁定在一起,形成單一的可執(zhí)行程序,并且與C代碼庫中的函數(shù)一起。
Linux對.c文件分辨進(jìn)行預(yù)處理,編譯,匯編三大步指令:
gcc -E test.c -o test.i? (預(yù)處理指令)
gcc -S test.i -o test.s??(編譯指令)
gcc -c?test.s-o test.o? ? (匯編指令)
第四個鏈接過程指令不需要寫:
默認(rèn)版的鏈接指令:gcc test.o?
因為操作系統(tǒng)會默認(rèn)從特定目錄中找所需要的第三方庫,若系統(tǒng)庫中沒有,則需要使用-l選項去鏈接:?
完整版的鏈接指令:gcc test.o??-l?[第三方庫]?
預(yù)處理指令:
gcc -E test.c -o test.i? (預(yù)處理指令)
????????預(yù)處理所做的工作就是:編譯器將.c文件中的頭文件從操作系統(tǒng)庫中找出來,然后拷貝頭文件的內(nèi)容到一個.i文件中,相當(dāng)于展開頭文件;此外將定義好的宏,條件編譯等指令帶入代碼中;將.c文件中的注釋全都注釋掉(計算機(jī)不需要知道你寫的注釋,沒用!它只需要代碼)
?通過上圖可知:.i文件的行數(shù)達(dá)到了近900行,百分之90多的內(nèi)容全是展開的頭文件的內(nèi)容
編譯指令:
gcc -S test.i -o test.s??(編譯指令)
這個過程所做的工作就是:編譯器對代碼進(jìn)行語法,詞法,語義的分析,將代碼從C類型轉(zhuǎn)換為高級匯編型語言。
匯編指令:
gcc -c?test.s-o test.o? ? (匯編指令)
該過程所做的工作就是將匯編代碼轉(zhuǎn)換為01二進(jìn)制代碼,直到這一步,計算機(jī)才能真正讀懂我們的代碼。
?二進(jìn)制內(nèi)容對我們來說就是看不懂的亂碼!
?此時,雖然該文件已經(jīng)能被計算機(jī)所讀懂,但是還仍不能被運行,缺少了一步鏈接過程。
?整體來說就是:test.c ---> test.i ---> test.s ---> test.o二進(jìn)制文件
接下來說一說鏈接過程:
? ? ? ? 在我們編寫的C代碼程序中,并沒有定義“printf,scanf”等函數(shù)的實現(xiàn)方式,在預(yù)編譯過程中包含的“stdio.h”頭文件中也只有該函數(shù)的聲明,而沒有定義函數(shù)的實現(xiàn),那么,那么是在哪里實現(xiàn)“printf”函數(shù)的呢?
? ? ? ? ?其實是系統(tǒng)把這些函數(shù)實現(xiàn)都被做到名為 libc.so.6 的庫文件中去了,在沒有特別指定時,gcc編譯器會到系統(tǒng)默認(rèn)的庫搜索路徑“/usr/lib”下進(jìn)行查找,也就直接鏈接到 libc.so.6 庫函數(shù)中去,這樣就能實現(xiàn)函 數(shù)“printf”了。
鏈接的本質(zhì): 無非是我們調(diào)用函數(shù)的時候和標(biāo)準(zhǔn)庫進(jìn)行關(guān)聯(lián),從而實現(xiàn)我們寫的程序文件
我們使用鏈接庫,有兩種方式:1.動態(tài)鏈接? ?2.靜態(tài)鏈接。
II.動靜態(tài)鏈接?
一.動態(tài)鏈接
? ? ? ?動態(tài)鏈接是把程序按照模塊拆分成各個相對獨立的部分,在程序運行時才將它們鏈接在一起形成一個完整的程序,而不是像靜態(tài)鏈接一樣把所有程序模塊都鏈接成一個單獨的可執(zhí)行文件。下面簡單介紹動態(tài)鏈接的過程:
????????假設(shè)現(xiàn)在有兩個程序program1.o和program2.o,這兩者共用同一個lib.o,假設(shè)首先運行程nrram1,系統(tǒng)首先加翻o0ram1.0當(dāng)系統(tǒng)發(fā)現(xiàn)program1.o中用到了lib.o,即program1.o依賴于lib.o.那么系統(tǒng)接著加載lib.o,如果program1.o和lib.o還依賴于其他目標(biāo)文件,則依次全部加載到內(nèi)存中。當(dāng)program2運行時,同樣的加載program2.o,然后發(fā)現(xiàn)program2.o依賴于lib.o,但是此時lib.o已經(jīng)存在于內(nèi)存中,這個時候就不再進(jìn)行重新加載lib.o,而是將內(nèi)存中已經(jīng)存在的lib.o映射到program2的虛擬地址空間中,從而進(jìn)行鏈接(這個鏈接程和靜態(tài)鏈接類似) 形成可執(zhí)行程序。
????????動態(tài)鏈接基本思想:對組成程序的目標(biāo)文件等到程序運行的時候才進(jìn)行鏈接也就是說,把鏈接這個過程推遲到了運行時再進(jìn)行,這就是動態(tài)鏈接的基本思想。
? ? ? ? 優(yōu)點就是:形成的可執(zhí)行程序小,節(jié)省內(nèi)存和磁盤占比空間。
二.靜態(tài)鏈接
??????靜態(tài)鏈接,因為每個可執(zhí)行程序中對所有需要的目標(biāo)文件都要有一份副本,所以如果多個程序?qū)ν瑐€目標(biāo)文件都有依賴,例如多個程序中都調(diào)用了print0函數(shù),則這多個程序中都會含有printf.o的底層實現(xiàn)方式,所以同一個目標(biāo)文件都在內(nèi)存中存在多個副本,原本一個目標(biāo)文件只需要一個函數(shù)的.o實現(xiàn)文件,現(xiàn)在由于其他目標(biāo)文件也有相同于自己文件中的函數(shù),產(chǎn)生多個冗余的函數(shù).o文件;還有就是更新比較困難,因為每當(dāng)庫函數(shù)的代碼修改了,這個時候就需要重新進(jìn)行編譯鏈接形成可執(zhí)行程序。但是靜態(tài)鏈接在可執(zhí)行程序中已經(jīng)具備了所有執(zhí)行程序所需要的任何東西,在執(zhí)行的時候運行速度快。
所以動態(tài)鏈接能解決這些問題: 彌補(bǔ)了靜態(tài)鏈接造成的浪費空間的缺點。
??
三.兩個鏈接的區(qū)別:
????????動態(tài)鏈接的優(yōu)缺點:
優(yōu)點1:即使需要每個程序都依賴同一個庫,但是該庫不會像靜態(tài)鏈接那樣在內(nèi)存中存在多分副本,而是這么多個程序在執(zhí)行時共享同一份副本;所以最終形成的可執(zhí)行文件體積小。
優(yōu)點2:更新也比較方便,更新時只需要替換原來的目標(biāo)文件,而無需將所有的程序再重新鏈接一遍。當(dāng)程序下一次運行時,新版本的目標(biāo)文件會被自動加載到內(nèi)存并且鏈接起來,程序就完成了升級的目標(biāo)。但是動態(tài)鏈接也是有缺點的,因為把鏈接推遲到了程序運行時,所以每次執(zhí)行程序都需要進(jìn)行鏈接,所以性能會有一定損失。
缺點1:無法獨立運行;可移植性差。
??
????????靜態(tài)鏈接的優(yōu)缺點:
優(yōu)點1:可移植性強(qiáng);可獨立運行。
缺點1:代碼冗余,可執(zhí)行文件體積大。
缺點2:如果所有使用的靜態(tài)庫發(fā)生改變,程序需要重新編譯。
????????據(jù)估算,動態(tài)鏈接和靜態(tài)鏈接相比,性能損失大約在5%以下。經(jīng)過實踐證明,這點性能損失用來換區(qū)程序在空間上的節(jié)省和程序構(gòu)建和升級時的靈活性是值得的。
III.動靜態(tài)庫
????????采用靜態(tài)鏈接方式實現(xiàn)鏈接操作的庫文件稱為靜態(tài)鏈接庫;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 用動態(tài)鏈接方式實現(xiàn)鏈接操作的庫文件稱為動態(tài)鏈接庫。
????????在 Linux 發(fā)行版系統(tǒng)中,靜態(tài)鏈接庫文件的后綴名通常用 .a 表示,動態(tài)鏈接庫的后綴名通常用 .so 表示。
????????GCC 編譯器生成可執(zhí)行文件時,默認(rèn)情況下會優(yōu)先使用動態(tài)鏈接庫實現(xiàn)鏈接操作,除非當(dāng)前系統(tǒng)環(huán)境中沒有程序文件所需要的動態(tài)鏈接庫,GCC 編譯器才會選擇相應(yīng)的靜態(tài)鏈接庫。如果兩種都沒有(或者 GCC 編譯器未找到》,則鏈接失敗。
????????
????????當(dāng)我門在編譯C/C++代碼的文件時,Linux默認(rèn)會使用C/C++動態(tài)標(biāo)準(zhǔn)庫進(jìn)行鏈接,系統(tǒng)中是沒有靜態(tài)庫的,需要我們親自安裝,如下是C,C++靜態(tài)庫的安裝指令:
安裝C語言靜態(tài)庫的指令: sudo yum install -static
試驗1:?
????????注意: gcc98k.c指今不帶后面的-o..,系統(tǒng)會默認(rèn)生成a.out(98k.c的可執(zhí)行文件),若帶了-o,就必須要寫自己命名的可執(zhí)行文件名字。
通過查看兩個方式生成的可執(zhí)行文件的大小:
?更加證明了動態(tài)鏈接要比靜態(tài)鏈接的性價比好的多。
安裝C++靜態(tài)庫的指令:
sudo yum install -y libstdc++ -static
?使用g++編譯器對.cpp文件進(jìn)行編譯,轉(zhuǎn)換成可執(zhí)行程序的指令:
試驗2:
file指令:
功能說明:辨識文件類型。
語法:file [選項] 文件或目錄...?
例1.使用file指令查看上面創(chuàng)建的兩個動靜態(tài)鏈接生成的可執(zhí)行文件類型:
ldd指令:
功能說明:用來打印或者查看程序運行所需的共享庫(共享庫就是動態(tài)庫!)。常用來解決程序因缺少某個庫文件而不能運行的一些問題。
語法:ldd [文件名]
如下:使用ldd指令查看可執(zhí)行文件:
總結(jié):
Linux下默認(rèn)形成可執(zhí)行程序,默認(rèn)使用的是動態(tài)庫。動態(tài)庫不需要下載(成本低,效率高) ! 只有靜態(tài)庫需要下載。
在linux下庫的命名:
動態(tài)庫的類型:? ? ?libXXXXXXX.so
靜態(tài)庫的類型:????????libyyyyyyy.a
完整的C動態(tài)庫,大小2156492字節(jié),全名: libc-2.17.so:
完整的C靜態(tài)庫,大小為5105516字節(jié)大小,全名: libc.?a:
所以:我們寫的代碼 + 庫提供的代碼=可執(zhí)行程序?文章來源:http://www.zghlxwxcb.cn/news/detail-765015.html
此外,vim編輯器不僅可以寫C,C++語言的代碼,還可以寫Java,Python語言的代碼喲!文章來源地址http://www.zghlxwxcb.cn/news/detail-765015.html
到了這里,關(guān)于Linux——gcc/g++編譯器的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!