前言 :
?在前面的文章里給大家介紹了vim的使用方法,學(xué)會了vim之后就可以進(jìn)行代碼的編寫工作,但vim僅僅只是一款文本編輯器,要想讓我們的代碼運(yùn)行起來,還需要使用今天給大家介紹的編譯工具:gcc/g++。其中g(shù)cc是針對C語言的編譯器,g++是針對C++的編譯器,他倆在使用形式上是相同的,所以今天主要以gcc為主,給大家介紹一下它們的使用方法,讓大家快速上手。
?編譯主要分為預(yù)處理、編譯、匯編、鏈接四個(gè)過程,下面將結(jié)合這四個(gè)具體過程,來介紹gcc的使用,同時(shí)會穿插介紹一些提升我們內(nèi)功的邊緣知識。
一、預(yù)處理
- 預(yù)處理的主要功能主要包括宏替換、頭文件展開、條件編譯、去注釋等。
- 預(yù)處理指令都是以#開頭的代碼行。
- 指令:
gcc -E test.c -o test.i
-
-E
:讓gcc在預(yù)處理結(jié)束后停止編譯過程。 -
-o
:將當(dāng)前編譯結(jié)果寫入到test.i文件中,.i
文件為經(jīng)過預(yù)處理的C源程序(注意:此時(shí)還是源程序)。
1.1 頭文件展開
?頭文件展開,就是把頭文件中的內(nèi)容拷貝到當(dāng)前的源代碼中,這就意味著,在編譯之前,系統(tǒng)中必須得有這個(gè)頭文件,那我怎么知道系統(tǒng)中有沒有呢?其實(shí)完全不用擔(dān)心,頭文件屬于開發(fā)環(huán)境的一部i分,在Windows環(huán)境中,我們使用的vs、dev等都叫做集成開發(fā)環(huán)境,集代碼編寫、編譯于一體,我們在下載這些工具的時(shí)候,會選擇一個(gè)開發(fā)包,這其實(shí)就是下載C有關(guān)的頭文件和庫文件。而Linux環(huán)境是專門供程序員使用的,所以在大多數(shù)Linux環(huán)境下,與開發(fā)環(huán)境有關(guān)的東西,如:代碼編輯器、代碼編譯器、頭文件/庫文件等,都已經(jīng)提前幫我們準(zhǔn)備好了,我們可以直接開始寫代碼。
?/usr/include/
目錄是Linux下gcc/g++頭文件的默認(rèn)搜索路徑,該路徑下有許多和開發(fā)相關(guān)的頭文件。
1.2 條件編譯
?條件編譯,在我們平時(shí)寫代碼時(shí)似乎很少出現(xiàn),但是它的作用我們可千萬不能忽視。想必大家在下載一些軟件的時(shí)候,會出現(xiàn)社區(qū)版、專業(yè)版等,一般而言,社區(qū)版的軟件會比專業(yè)版的少一些功能。少的這些功能就是通過條件編譯裁剪掉的,如果沒有條件編譯,那針對每一個(gè)版本,廠商都需要寫一份對應(yīng)的代碼,那在維護(hù)的時(shí)候就非常麻煩,很可能會出現(xiàn)對社區(qū)版的修改了,而對專業(yè)版的沒改。但是有了條件編譯,廠商從始至終只需要維護(hù)一份代碼即可,對于社區(qū)版只要對專業(yè)版的代碼進(jìn)行條件編譯,裁剪掉相應(yīng)的功能即可。
小Tips:預(yù)處理后得到的.i
文件任然是C語言,只不過和我們的源碼相比變得更干凈了而已。
二、編譯
- 在這個(gè)階段,gcc首先首先要檢查代碼的規(guī)范性,是否有語法錯(cuò)誤,以確定代碼實(shí)際要做的工作,在檢查無誤后,gcc把代碼翻譯成匯編語言。
- 指令:
gcc -S test.i -o test.s
三、匯編
- 匯編階段是把編譯生成的
.s
文件中的匯編指令轉(zhuǎn)換成機(jī)器可以識別的二進(jìn)制,這個(gè)二進(jìn)制文件也被叫做可重定位目標(biāo)二進(jìn)制文件,簡稱目標(biāo)文件。 - 指令:
gcc -c test.s -o test.o
四、鏈接
- 鏈接階段是將目標(biāo)文件和庫文件進(jìn)行鏈接,形成可執(zhí)行程序
- 指令:
gcc test.o -o mytest
?有時(shí)候,我們會在程序當(dāng)中引用、調(diào)用其他的外部子程序,或者是利用其他軟件提供的函數(shù)功能,這個(gè)時(shí)候,我們就必須要在編譯過程中將該函數(shù)庫加進(jìn)去,如此一來,編譯器就可以將所有的程序代碼與函數(shù)庫做一個(gè)鏈接,以生成正確的執(zhí)行文件。
4.1 什么是庫?
?上面提到了庫和庫函數(shù)的概念。舉個(gè)簡單例子:大家在最開始學(xué)習(xí)C語言的時(shí)候,一定用過printf
函數(shù),來向顯示器上打印一串字符,當(dāng)時(shí)我們只知道,只要在我們代碼的開頭寫上一句#include <stdio.h>
,printf就能使用了?,F(xiàn)在我們知道stdio.h
是一個(gè)頭文件,里面放的都是一些聲明,因?yàn)檫@個(gè)頭文件里有printf
函數(shù)的聲明,所以包上它后,我們就能去使用printf這個(gè)函數(shù)。printf的具體實(shí)現(xiàn)方法其實(shí)是放在庫中的,可以這么說:庫給我們提供方法的實(shí)現(xiàn),庫其實(shí)就是把源文件,經(jīng)過一定的翻譯,然后打包,只給用戶提供一個(gè)文件,不用給我們提供太多的源文件,也可以達(dá)到隱藏源文件的目的,同時(shí),庫也避免了程序員自己去造輪子。所以這里的printf就是我們所說的庫函數(shù)。鏈接階段就是把我們寫的源代碼編譯得到的目標(biāo)文件與庫進(jìn)行鏈接,因?yàn)槲覀冇玫氖荂語言,所以默認(rèn)鏈接的是C語言標(biāo)準(zhǔn)庫。庫本質(zhì)上是一個(gè)文件,存在系統(tǒng)的特定目錄下。絕大多數(shù)的函數(shù)庫都放在/usr/lib
、/lib
目錄下。
?上圖展示的libc.so.6
就是C語言的標(biāo)準(zhǔn)庫。
4.2 庫的分類
?庫分為兩類:動態(tài)庫和靜態(tài)庫。其中Linux環(huán)境下,動態(tài)庫的后綴是.so
,靜態(tài)庫的后綴是.a
。在Windows環(huán)境下,動態(tài)庫的后綴是.dll
,靜態(tài)庫的后綴是.lib
。所有的庫文件,都遵守相同的命名規(guī)則,即:libname.后綴.xxx
。
小Tips:gcc編譯器會默認(rèn)找到C的標(biāo)準(zhǔn)庫,它會把我們寫的源代碼經(jīng)過編譯得到的目標(biāo)文件與庫文件進(jìn)行鏈接。這也是為什么gcc不能去編譯C++的源文件,因?yàn)間cc默認(rèn)找的是C的標(biāo)準(zhǔn)庫,它找不到C++的庫。
4.3 目標(biāo)文件和庫是如何鏈接的?
?總體上,鏈接分為兩類:動態(tài)鏈接和靜態(tài)鏈接。
4.3.1 動態(tài)鏈接
?將目標(biāo)文件與動態(tài)庫進(jìn)行鏈接,就叫做動態(tài)鏈接。動態(tài)庫就像是一個(gè)網(wǎng)吧,任何人想上網(wǎng)了,都可以去到這個(gè)網(wǎng)吧里。即:動態(tài)庫是被所有程序所共享的,一般也被叫做共享庫。這意味著,動態(tài)庫只需要一個(gè)就夠了,它可以滿足所有程序的需求。
?動態(tài)庫共享的特點(diǎn),導(dǎo)致動態(tài)庫不能丟失,就像網(wǎng)吧被查封了,人們就不能去上網(wǎng)一樣。一旦對應(yīng)的動態(tài)庫丟失,影響的不只是一個(gè)程序,可能會導(dǎo)致多個(gè)程序都無法正常運(yùn)行。
- 指令
ldd 可執(zhí)行程序
,可以查看一個(gè)可執(zhí)行程序所依賴的動態(tài)庫。
?Linux中,編譯形成可執(zhí)行程序,優(yōu)先采用動態(tài)鏈接。
4.3.2 靜態(tài)鏈接
?將目標(biāo)文件與靜態(tài)庫進(jìn)行鏈接,就叫做靜態(tài)鏈接。靜態(tài)庫就像電腦商城,當(dāng)有人有上網(wǎng)需求時(shí),會到電腦商城去買一臺專屬的電腦,只供自己使用。在編譯器使用靜態(tài)庫進(jìn)行靜態(tài)鏈接的時(shí)候,會將自己的方法拷貝到目標(biāo)程序中,該程序以后不再依賴靜態(tài)庫。
gcc test.c -o mytest-static -static
- 其中
-static
表示執(zhí)行靜態(tài)鏈接,前提是有靜態(tài)庫。 -
yum install -y glibc-static
:安裝C靜態(tài)庫
?gcc默認(rèn)優(yōu)先使用動態(tài)庫,如果我們沒有動態(tài)庫,只有靜態(tài)庫,也是可以的,-static
的本質(zhì)就是改變優(yōu)先級。鏈接的過程,不一定是純的全是動態(tài)鏈接或者靜態(tài)鏈接,二者可以同時(shí)出現(xiàn),但是如果加了-static
選項(xiàng),那么會把所有的鏈接都變成靜態(tài)鏈接。
-
file mytest
:查看mytest這個(gè)可執(zhí)行程序采用的是什么鏈接。
4.4 動靜態(tài)鏈接的優(yōu)缺點(diǎn)對比
優(yōu)點(diǎn) | 缺點(diǎn) | |
---|---|---|
動態(tài)庫 | 有效的節(jié)省資源(磁盤空間、內(nèi)存空間、網(wǎng)絡(luò)空間等) | 一旦缺失,所有程序都無法運(yùn)行 |
靜態(tài)庫 | 不依賴庫,編譯成功的可執(zhí)行程序,可以獨(dú)立執(zhí)行,不需要再向外部要求讀取庫函數(shù)中的內(nèi)容 | 體積大,比較消耗資源 |
五、Debug&&release
?Debug是開發(fā)者模式,而用戶最終使用的是release。Debug模式下的代碼,可以被追蹤、調(diào)試,因?yàn)樵贒ebug模式下形成的可執(zhí)行程序,里面添加了debug信息。這意味著,以Debug模式下得到的可執(zhí)行程序,一定比release模式下得到的可執(zhí)行程序要大。
?gcc編譯器,默認(rèn)是以release的模式編譯得到可執(zhí)行程序,要在Debug模式下,編譯得到可執(zhí)行程序,需要加-g
選項(xiàng),如下:
gcc test.c -o mytest-Debug -g
-
readelf -S mytest
:把對應(yīng)的可執(zhí)行程序以段的形式讀取出來。 -
readelf -S mytest-Debug | grep debug
:篩選出與Debug有關(guān)的段。
文章來源:http://www.zghlxwxcb.cn/news/detail-553396.html
?今天的分享到這里就結(jié)束啦!如果覺得文章還不錯(cuò)的話,可以三連支持一下,您的支持就是春人前進(jìn)的動力!文章來源地址http://www.zghlxwxcb.cn/news/detail-553396.html
到了這里,關(guān)于【Linux】基礎(chǔ)開發(fā)工具——gcc/g++篇的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!