寫在前面:
上一篇博客, 我們學(xué)習(xí)了vim編輯工具,學(xué)會(huì)了怎么寫代碼,
這篇文章,我將分享代碼該怎么編譯的問題。
目錄
寫在前面:
1. gcc和g++介紹
2. gcc是如何編譯程序的
1. 預(yù)處理
2. 編譯
3. 匯編
4. 鏈接
3. gcc的選項(xiàng)介紹
4. 我們使用的函數(shù)是哪來的
5. 我們的.o文件和庫是如何鏈接的?
6. debug和release????????
寫在最后:
1. gcc和g++介紹
我們通過gcc和g++編譯代碼,gcc主要編譯C語言,g++則可以編譯C語言和C++,
gcc和g++的選項(xiàng)基本一致,所以我介紹gcc的選項(xiàng),g++直接平替過去就行。
這里我們話不多說,直接演示一下:
比如說,我們寫了一段代碼:
我們可以用gcc來編譯一下:
我們發(fā)現(xiàn),使用gcc編譯的時(shí)候,他會(huì)生成默認(rèn)叫做a.out的可執(zhí)行程序文件,
其實(shí)具體是什么名稱或者說后綴并不重要,能不能執(zhí)行,
只取決于他有沒有可執(zhí)行權(quán)限,我們在權(quán)限那篇文章已經(jīng)講過了。
當(dāng)然我們也可以寫C++代碼,C++的后綴名有不少,
可以用.cpp,不過我更習(xí)慣用.cc作為我的后綴名:
比如說我現(xiàn)在寫了一段C++代碼,用g++編譯一下:
編譯的使用,默認(rèn)形成的可執(zhí)行程序也是a.out。
補(bǔ)充:
g++是可以直接編譯C語言的,他就直接按照C語言的形式編譯。
2. gcc是如何編譯程序的
1. 預(yù)處理
預(yù)處理主要干這幾件事:
a. 去注釋
b. 頭文件展開
c. 條件編譯
d. 宏替換
這是我們文件先在包含的一段代碼,存在注釋,頭文件,條件編譯和宏替換。
?我們需要使用gcc的一個(gè)選項(xiàng):-E
以及一個(gè)選項(xiàng) -o 指定我們形成的文件的名字。
對這段程序進(jìn)行預(yù)處理操作:
我們成功形成了文件,打開看看:
我們可以看到,左邊的使我們的源代碼,右邊是預(yù)處理之后的程序,
可以看到,預(yù)處理該干的事情都干了:
?這個(gè)就是預(yù)處理的過程,我們學(xué)習(xí)C語言的階段應(yīng)該是學(xué)習(xí)過了。
補(bǔ)充內(nèi)容:
我們?yōu)槭裁茨茉赪indows或者說Linux下進(jìn)行C/C++或者其他形式的開發(fā)呢?
我們系統(tǒng)中一定要提前或者后續(xù)安裝上,C/C++開發(fā)相關(guān)的頭文件,庫文件。
C/C++開發(fā)環(huán)境不僅僅指的是vs,gcc,g++,更重要的是語言本身的頭文件和庫文件。
所以Linux是提前給我們裝好的C/C++的頭文件,
那么在哪里呢?
?這個(gè)路徑就是gcc,g++在Linux下默認(rèn)搜索頭文件的時(shí)候,搜索的路徑。
我們能在這里找到我們常用的頭文件。
我們可以vim一下進(jìn)入stdio.h文件里面看看:
總共就是九百多行,我們剛剛展開的頭文件,也是差不多這個(gè)行數(shù)。
其實(shí)他就是把這一段拷貝到了我們的源文件當(dāng)中。
所以我們在下載所謂的VS2019這些編譯器,選擇開發(fā)包的時(shí)候,
就是在同步下載C/C++對應(yīng)的頭文件和庫文件。?
?補(bǔ)充內(nèi)容:
gcc是支持對文件進(jìn)行文本級內(nèi)容修改的,
比如說:?
我們編譯的時(shí)候,定義一個(gè)RUN的宏:
?這個(gè)宏確實(shí)是定義進(jìn)去了。
2. 編譯
編譯形成匯編代碼:
這些其實(shí)就是匯編代碼。?
3. 匯編
再將匯編代碼轉(zhuǎn)成機(jī)器可以識別的形式,
說人話就是形成二進(jìn)制:
我們就能看到這些意義不明的二進(jìn)制亂碼了。
當(dāng)然,我們也可以用一些二進(jìn)制查看工具:
?可以查看一下二進(jìn)制的內(nèi)容:
4. 鏈接
最后我們就完成了:
獲得了可執(zhí)行程序。?
3. gcc的選項(xiàng)介紹
-o 選項(xiàng),
我們剛剛已經(jīng)對-o 選項(xiàng)有了一個(gè)基本的了解,
實(shí)際上,他形成的自定義的文件名我們是可以隨便修改的。
這里我就不舉例子了,我命名的是mytest ,你們想叫什么都行。
只要在-o 后面跟上我們的可執(zhí)行程序即可。
那么,
我們對程序進(jìn)行預(yù)處理的時(shí)候,使用的 -E 選項(xiàng)是什么意思呢?
作用:告訴gcc,從現(xiàn)在開始進(jìn)行程序的翻譯,將預(yù)處理工作做完后就不要往后走了。
那 -S 的選項(xiàng)又是什么意思呢?
從現(xiàn)在開始進(jìn)行程序的翻譯,將編譯工作做完就停下來。
那 -c 的選項(xiàng)是什么呢?以此類推:
從現(xiàn)在開始進(jìn)行程序的翻譯,將匯編工作做完就停下來。
而匯編形成的test.o文件,又稱為可重定位目標(biāo)二進(jìn)制文件,簡稱目標(biāo)文件。
(其實(shí)Windows下的.obj文件就是目標(biāo)文件)
而這個(gè)文件是不能獨(dú)立執(zhí)行的,需要通過鏈接才能執(zhí)行。
最后進(jìn)行g(shù)cc鏈接,
其實(shí)就是將可重定位目標(biāo)二進(jìn)制文件,和庫進(jìn)行鏈接形成可執(zhí)行程序。
他們的選項(xiàng):-E -S -c ,如果你記不住的話,可以看看鍵盤左上角的ESC鍵(記得c是小寫就行)
4. 我們使用的函數(shù)是哪來的
比如說這段代碼里的printf函數(shù):
我們想要調(diào)用一個(gè)函數(shù),我們使用函數(shù)名來調(diào)用,
我們需要看到函數(shù)的聲明,printf這個(gè)函數(shù)的聲明在stdio.h這個(gè)頭文件里面,
那他的定義呢?或者說這個(gè)函數(shù)的實(shí)現(xiàn)呢?是誰給我們提供的?
實(shí)際上是有一個(gè)庫來給我們提供方法的實(shí)現(xiàn)。
這里用的是C語言,所以其實(shí)就是C語言的標(biāo)準(zhǔn)庫來提供實(shí)現(xiàn)。
我們通過這個(gè) 路徑就能找到這個(gè)C語言標(biāo)準(zhǔn)庫,
所以C語言標(biāo)準(zhǔn)庫本質(zhì)上其實(shí)就是一個(gè)文件。
而在Linux下,.so結(jié)尾的是動(dòng)態(tài)庫,.a結(jié)尾的是靜態(tài)庫。
而在Windows下:.dll后綴是動(dòng)態(tài)庫,.lib后綴是靜態(tài)庫。
一般來說,庫是有自己的命名規(guī)則的:libname.so.xxx,
所以上面的libc,c就是名字,.6是他的版本。
方法的實(shí)現(xiàn)就是在庫當(dāng)中的,
庫其實(shí)就是把源文件,經(jīng)過一定的編譯(翻譯),然后打包,只給你一個(gè)文件即可。
總結(jié):
頭文件提供方法的聲明,庫文件提供方法的實(shí)現(xiàn) + 你的代碼 = 你的軟件。
(不用我們重復(fù)造輪子,站在巨人的肩膀上)
5. 我們的.o文件和庫是如何鏈接的?
有兩種方法:
1. 動(dòng)態(tài)鏈接
2. 靜態(tài)鏈接
先說動(dòng)態(tài)庫,
動(dòng)態(tài)庫也叫做共享庫,是多個(gè)程序共享的一個(gè)庫,他們是構(gòu)建了一種鏈接的關(guān)系。
比如說我們上面剛剛演示的那個(gè)C語言的庫,如果我們把它刪了,
那我們的這個(gè)可執(zhí)行程序就無法執(zhí)行:
我們用C語言寫的可執(zhí)行程序,默認(rèn)調(diào)用的都是動(dòng)態(tài)庫,就都沒辦法執(zhí)行了。
再來說靜態(tài)庫,
在使用靜態(tài)庫的時(shí)候,實(shí)際上是把靜態(tài)庫的內(nèi)容也拷進(jìn)了程序里面,
這樣使用的時(shí)候就不需要通過鏈接去找?guī)熨Y源,以后也就不用依賴靜態(tài)庫了,
而是可以自己使用了。
現(xiàn)在我們寫代碼驗(yàn)證一下:
?這是我們之前直接用gcc編譯出來的可執(zhí)行程序,
可以看出:
在Linux中,編譯形成的可執(zhí)行程序,默認(rèn)采用的就是動(dòng)態(tài)鏈接 -- 要求系統(tǒng)提供動(dòng)態(tài)庫
我們使用:gcc test.cc -o mytest_static -static
使用靜態(tài)鏈接編譯一個(gè)程序,
?可以看見他沒有動(dòng)態(tài)鏈接,
我們通過 ll 查看更多信息:
可以明顯看出靜態(tài)鏈接的可執(zhí)行程序體積變大了,
在Linux中,如果要按照靜態(tài)鏈接的方式,形成可執(zhí)行程序,需要添加-static選項(xiàng),
也就是系統(tǒng)需要提供靜態(tài)庫才能使用。
不過我們Linux下默認(rèn)只裝了動(dòng)態(tài)庫,靜態(tài)庫需要自己安裝。
輸入:sudo yum install -y glibc-static
就能直接安裝C語言的靜態(tài)庫。
輸入:sudo yum install -y libstdc++-static
就能直接安裝C++的靜態(tài)庫。
補(bǔ)充:
1. 如果我們沒有靜態(tài)庫,能不能使用-static進(jìn)行編譯呢?不能
2. 如果我們沒有動(dòng)態(tài)庫,但是有靜態(tài)庫,能不能直接gcc?能
gcc默認(rèn)優(yōu)先動(dòng)態(tài)庫,如果沒有動(dòng)態(tài)庫也會(huì)用靜態(tài)庫編譯,-static的本質(zhì):改變優(yōu)先級
3. 其實(shí)我們使用的這些庫,不一定是純的全部動(dòng)態(tài)或者靜態(tài)鏈接,是混合的。?
總結(jié):
動(dòng)靜態(tài)庫的優(yōu)缺點(diǎn):
動(dòng)態(tài)庫是共享庫,有效的節(jié)省資源(磁盤空間,內(nèi)存空間,網(wǎng)絡(luò)空間等)
靜態(tài)庫,不依賴庫,程序可以獨(dú)立運(yùn)行,但是體積大,比較消耗資源。
6. debug和release????????
debug版本是我們平時(shí)寫的版本,release版本是上線版本,
而debug版本可以被追蹤調(diào)試
那debug為啥能被調(diào)試呢?
因?yàn)樵谛纬煽蓤?zhí)行程序的時(shí)候,添加了debug信息。
我們可以在使用gcc的時(shí)候加入 -g 選項(xiàng)添加調(diào)試信息:
gcc test.cc -o mytest_debug -g
?可以看見他的可執(zhí)行程序也變大了一點(diǎn)點(diǎn),
其實(shí)就是里面添加了調(diào)試信息。
這里再介紹一個(gè)指令來查看我們可執(zhí)行程序二進(jìn)制的構(gòu)成,
來觀察我們往里面添加的調(diào)試信息:
我們輸入:
readelf -S mytest | grep -i debug
查看mytest的調(diào)試信息,可以觀察到是啥都沒有的,
而輸入:
readelf -S mytest_debug | grep -i debug
我們可以直觀的看到這些調(diào)試信息。?
寫在最后:
以上就是本篇文章的內(nèi)容了,感謝你的閱讀。
如果感到有所收獲的話可以給博主點(diǎn)一個(gè)贊哦。文章來源:http://www.zghlxwxcb.cn/news/detail-496732.html
如果文章內(nèi)容有遺漏或者錯(cuò)誤的地方歡迎私信博主或者在評論區(qū)指出~文章來源地址http://www.zghlxwxcb.cn/news/detail-496732.html
到了這里,關(guān)于【Linux環(huán)境基礎(chǔ)開發(fā)工具】編譯器-gcc/g++的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!