濟(jì)寧網(wǎng)站建設(shè)神華關(guān)鍵詞優(yōu)化包含
文章目錄
- 1.編譯器gcc/g++
- 1.1C語(yǔ)言程序的翻譯過(guò)程
- 1.預(yù)處理
- 2.編譯
- 3.匯編
- 4. 鏈接
- 1.2 鏈接方式與函數(shù)庫(kù)
- 1.動(dòng)態(tài)鏈接與靜態(tài)鏈接
- 2.動(dòng)態(tài)庫(kù)與靜態(tài)庫(kù)
- 1.3 gcc與g++的使用
- 2.調(diào)試器gdb
- 2.1debug和release
- 2.2gdb的安裝
- 2.3gdb的使用
- 2.4gdb的常用指令
- 3.總結(jié)
1.編譯器gcc/g++
1.1C語(yǔ)言程序的翻譯過(guò)程
1.預(yù)處理
在此階段做的事情:
- 頭文件展開:把我們編寫的代碼中的包含頭文件的代碼替換成頭文件本身
- 刪除所有的注釋
- #define定義的符號(hào)和宏全部替換
- 執(zhí)行條件編譯
在Linux下,我們可以通過(guò)指令讓gcc只執(zhí)行預(yù)處理操作
gcc -E test.c -o test.i
# -E 表示從現(xiàn)在開始,進(jìn)行程序的翻譯,當(dāng)預(yù)處理結(jié)束時(shí)停下來(lái)
# -o 表示指明產(chǎn)生的文件的名稱
可以看到經(jīng)過(guò)預(yù)編譯之后,#include包含頭文件的代碼沒(méi)有了,但是文件中多了幾百行,這些多的就是頭文件的內(nèi)容被拷貝進(jìn)來(lái)了,注釋部分被刪除,宏定義被替換了,條件編譯也轉(zhuǎn)變成了執(zhí)行過(guò)后的結(jié)果。
2.編譯
此階段做的事情:
- 語(yǔ)法分析
- 詞法分析
- 語(yǔ)義分析
- 符號(hào)匯總
最終的結(jié)果就是把C語(yǔ)言代碼變成匯編語(yǔ)言代碼
在Linux下需要執(zhí)行的指令是:
gcc -S test.i -o test.s
# -S 表示從現(xiàn)在開始,執(zhí)行程序的翻譯,做完編譯工作之后,變成匯編代碼就停下來(lái)
# 變成的匯編代碼的后綴名是.s
打開test.s之后我們可以發(fā)現(xiàn),里面的代碼意見已經(jīng)變成匯編指令了。
3.匯編
此階段做的事情:把匯編代碼變成二進(jìn)制(這里的二進(jìn)制不是可執(zhí)行的,叫做二進(jìn)制目標(biāo)文件)
在Linux下需要執(zhí)行的指令是:
gcc -c test.s -o test.o
# -c 表示從現(xiàn)在開始,進(jìn)行程序的翻譯,做完匯編工作,變成可重定向的目標(biāo)二進(jìn)制,就停下來(lái)
# 重定向的目標(biāo)二進(jìn)制文件的后綴名是.o
可以看到,此時(shí)文件內(nèi)已經(jīng)變成了我們看不懂的二進(jìn)制代碼,當(dāng)他以二進(jìn)制的形式打開時(shí),是這樣的
4. 鏈接
此階段做的事情:把本地編寫的代碼和c標(biāo)準(zhǔn)庫(kù)中的代碼合并,形成可執(zhí)行的二進(jìn)制文件
- 合并段表:編譯器會(huì)把在匯編階段生成的多個(gè)目標(biāo)文件中相同格式的數(shù)據(jù)合并在一起,最終形成一個(gè) .exe 文件。
- 符號(hào)表的合并和重定位:符號(hào)表的合并是指編譯器會(huì)把在匯編階段生成的多個(gè)符號(hào)表合并為一個(gè)符號(hào)表;重定位則是指當(dāng)同一個(gè)符號(hào)出現(xiàn)在兩個(gè)符號(hào)表中時(shí),編譯器會(huì)選取其中和有效地址相關(guān)的那一個(gè),舍棄另外一個(gè)
在Linux下需要執(zhí)行的指令是:
gcc test.o -o mytest
# 鏈接階段是程序翻譯的最后一個(gè)階段,不需要加任何選項(xiàng)
# 默認(rèn)生成的可執(zhí)行文件的文件名是a.out,我們可以通過(guò)-o選項(xiàng)指定
可以看到,產(chǎn)生的文件mytest就是可執(zhí)行的程序。
注:
- 對(duì)于上述的幾個(gè)Linux下gcc指令的選項(xiàng)和產(chǎn)生文件的后綴名,這里有一個(gè)方便記憶的小技巧,預(yù)編譯、編譯、鏈接的選項(xiàng)分別是ESc,對(duì)應(yīng)著鍵盤左上角的按鍵Esc,產(chǎn)生的文件后綴名是iso,對(duì)應(yīng)著光盤映像文件的后綴名
- 上述的分段執(zhí)行只是為了方便我們能夠更加細(xì)致的看到程序翻譯的過(guò)程,在實(shí)際使用gcc的時(shí)候,只需要使用指令
gcc 原文件名 -o 產(chǎn)生的可執(zhí)行文件名
或者gcc 原文件名
即可。
1.2 鏈接方式與函數(shù)庫(kù)
1.動(dòng)態(tài)鏈接與靜態(tài)鏈接
我們?cè)趯懘a的過(guò)程中,會(huì)經(jīng)常用到庫(kù)函數(shù),類似printf,scanf,strlen等函數(shù),這些函數(shù)在我們的代碼中只是調(diào)用了它們,并沒(méi)有實(shí)現(xiàn),那么是誰(shuí)實(shí)現(xiàn)的呢?答案是庫(kù)函數(shù),是別人預(yù)先寫好的
同時(shí),程序在預(yù)處理、編譯和匯編階段處理的都是我們自己編寫的代碼,只有在鏈接的時(shí)候,庫(kù)函數(shù)的實(shí)現(xiàn)才會(huì)和我們的代碼關(guān)聯(lián)起來(lái) (符號(hào)表的重定位);所以,鏈接的本質(zhì)是我們?cè)谡{(diào)用庫(kù)函數(shù)時(shí)如何與標(biāo)準(zhǔn)庫(kù)相關(guān)聯(lián)的問(wèn)題
程序的鏈接方式一共有兩種:動(dòng)態(tài)鏈接與靜態(tài)鏈接
動(dòng)態(tài)鏈接是指執(zhí)行代碼時(shí),如果遇到庫(kù)函數(shù)調(diào)用就跳轉(zhuǎn)到動(dòng)態(tài)庫(kù)中對(duì)應(yīng)函數(shù)的定義處,然后執(zhí)行該函數(shù),執(zhí)行完畢后再跳轉(zhuǎn)回原程序并繼續(xù)往下執(zhí)行;它的優(yōu)點(diǎn)是形成的可執(zhí)行程序小,缺點(diǎn)是受到動(dòng)態(tài)庫(kù)變動(dòng) (刪除、升級(jí)等) 的影響
靜態(tài)鏈接則是直接將本程序內(nèi)部要使用的庫(kù)函數(shù)從對(duì)應(yīng)的靜態(tài)庫(kù)中拷貝一份過(guò)來(lái);它的優(yōu)點(diǎn)是不與靜態(tài)庫(kù)產(chǎn)生關(guān)聯(lián),即不受靜態(tài)庫(kù)變動(dòng) (刪除、升級(jí)等) 的影響;缺點(diǎn)是形成的可執(zhí)行程序非常大。
2.動(dòng)態(tài)庫(kù)與靜態(tài)庫(kù)
函數(shù)庫(kù)是一些事先寫好的,用于給別人復(fù)用的函數(shù)的集合,函數(shù)庫(kù)一般分為靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)兩種
靜態(tài)庫(kù)是指在編譯鏈接時(shí),把包含的庫(kù)文件全部拷貝到可執(zhí)行文件中,然后在運(yùn)行時(shí)就不再需要庫(kù)文件了,但是由于拷貝了全部?jī)?nèi)容,所以生成的文件會(huì)很大。靜態(tài)庫(kù)在Linux下的后綴名是.a,在Windows下后綴名是.lib
動(dòng)態(tài)庫(kù):也叫共享庫(kù),與靜態(tài)庫(kù)相反,在編譯鏈接時(shí)并沒(méi)有把庫(kù)文件的代碼加入到可執(zhí)行文件中,而是在程序執(zhí)行時(shí)由運(yùn)行時(shí)鏈接文件加載庫(kù),這樣可以節(jié)省系統(tǒng)的開銷。動(dòng)態(tài)庫(kù)一般后綴名為.so,在Windows下后綴名是.dll
那么,我們驗(yàn)證一下,我們?cè)贚inux下編譯是怎么鏈接的
驗(yàn)證方法:使用file指令可以看到調(diào)用的庫(kù)是動(dòng)態(tài)庫(kù)還是靜態(tài)庫(kù)
我們可以看到,gcc默認(rèn)的鏈接方式是動(dòng)態(tài)鏈接,那么怎么讓它使用靜態(tài)鏈接呢?只需要在編譯指令后面加上-static
這個(gè)時(shí)候,我們查看一下兩個(gè)文件的詳細(xì)信息
可以看到,使用靜態(tài)鏈接產(chǎn)生的可執(zhí)行文件,大小比動(dòng)態(tài)鏈接產(chǎn)生的文件大得多。
這里補(bǔ)充一點(diǎn)非常重要的事情:一定不要?jiǎng)h除系統(tǒng)中的C動(dòng)態(tài)庫(kù),因?yàn)長(zhǎng)inux系統(tǒng)中的基本上所有指令都是使用C語(yǔ)言寫的,如果沒(méi)有C動(dòng)態(tài)庫(kù),會(huì)導(dǎo)致很多指令都無(wú)法使用,最終的解決方案只能是重裝系統(tǒng)。
1.3 gcc與g++的使用
gcc 選項(xiàng)
- -E 只激活預(yù)處理,這個(gè)不生成文件,你需要把它重定向到一個(gè)輸出文件里面
- -S 編譯到匯編語(yǔ)言不進(jìn)行匯編和鏈接
- -c 編譯到目標(biāo)代碼
- -o 文件輸出到 文件
- -static 此選項(xiàng)對(duì)生成的文件采用靜態(tài)鏈接
- -g 生成調(diào)試信息。GNU 調(diào)試器可利用該信息。
- -shared 此選項(xiàng)將盡量使用動(dòng)態(tài)庫(kù),所以生成文件比較小,但是需要系統(tǒng)由動(dòng)態(tài)庫(kù).
- -O3 編譯器的優(yōu)化選項(xiàng)的4個(gè)級(jí)別,-O0表示沒(méi)有優(yōu)化,-O1為缺省值,-O3優(yōu)化級(jí)別最高
- -w 不生成任何警告信息。
- -Wall 生成所有警告信息
我們?cè)谑褂渺o態(tài)鏈接的方式編譯的時(shí)候,可能會(huì)發(fā)現(xiàn)報(bào)錯(cuò),因?yàn)橛胁糠諰inux機(jī)器沒(méi)有安裝C靜態(tài)庫(kù),所以需要我們手動(dòng)安裝
# 手動(dòng)安裝C靜態(tài)庫(kù)
sudo yum install -y glibc-static
同時(shí),部分Linux機(jī)器也是沒(méi)有安裝g++的,也需要我們手動(dòng)安裝
# 安裝g++
sudo yum install -y gcc-c++
# 安裝c++靜態(tài)庫(kù)
sudo yum install -y libstdc++-static
2.調(diào)試器gdb
2.1debug和release
debug和release是程序編譯的類型版本,debug是調(diào)試版本,其中包含了程序的調(diào)試信息,release是程序的發(fā)布版本,其中沒(méi)有調(diào)試信息,并且進(jìn)行了部分優(yōu)化(例如對(duì)死循環(huán)的優(yōu)化)。
可以看到,debug版本比release版本要大一點(diǎn),其中多的內(nèi)容就是調(diào)試信息,所以gdb調(diào)試必須要在debug模式下調(diào)試,如果是release版本下不可執(zhí)行調(diào)試
會(huì)顯示no debugging symbols found(沒(méi)有找到調(diào)試標(biāo)志)
Linux下gcc/g++編譯出來(lái)的程序默認(rèn)是release版本
到這里我們總結(jié)一下之前所學(xué)到的關(guān)于Linux下的一些默認(rèn)行為
gcc/g++的默認(rèn)行為
默認(rèn)連接方式是動(dòng)態(tài)連接(靜態(tài)鏈接需要加-static)
默認(rèn)編譯版本是release(編譯debug版本需要加-g)
vim的默認(rèn)行為
- 打開后的默認(rèn)模式是命令模式
2.2gdb的安裝
sudo yum install -y gdb
2.3gdb的使用
第一步:使用-g指令編譯源代碼,產(chǎn)生debug版本的可執(zhí)行程序
第二步:執(zhí)行
gdb FileName
進(jìn)入調(diào)試
第三步:輸入調(diào)試指令進(jìn)行調(diào)試即可
第四步:
ctrl+d
或者q/quit
退出調(diào)試
2.4gdb的常用指令
- list/l 行號(hào):顯示binFile源代碼,接著上次的位置往下列,每次列10行。
- list/l 函數(shù)名:列出某個(gè)函數(shù)的源代碼。
- r或run:運(yùn)行程序。
- n 或 next:單條執(zhí)行。
- s或step:進(jìn)入函數(shù)調(diào)用
- break(b) 行號(hào):在某一行設(shè)置斷點(diǎn)
- break 函數(shù)名:在某個(gè)函數(shù)開頭設(shè)置斷點(diǎn)
- info break :查看斷點(diǎn)信息。
- finish:執(zhí)行到當(dāng)前函數(shù)返回,然后挺下來(lái)等待命令
- print§:打印表達(dá)式的值,通過(guò)表達(dá)式可以修改變量的值或者調(diào)用函數(shù)
- p 變量:打印變量值。
- set var:修改變量的值
- continue(或c):從當(dāng)前位置開始連續(xù)而非單步執(zhí)行程序
- run(或r):從開始連續(xù)而非單步執(zhí)行程序
- delete breakpoints:刪除所有斷點(diǎn)
- delete breakpoints n:刪除序號(hào)為n的斷點(diǎn)
- disable breakpoints:禁用斷點(diǎn)
- enable breakpoints:啟用斷點(diǎn)
- info(或i) breakpoints:參看當(dāng)前設(shè)置了哪些斷點(diǎn)
- display 變量名:跟蹤查看一個(gè)變量,每次停下來(lái)都顯示它的值
- undisplay:取消對(duì)先前設(shè)置的那些變量的跟蹤
- until X行號(hào):跳至X行
- breaktrace(或bt):查看各級(jí)函數(shù)調(diào)用及參數(shù)
- info(i) locals:查看當(dāng)前棧幀局部變量的值
- quit:退出gdb
下面會(huì)使用實(shí)例來(lái)演示部分指令
list/l 行號(hào):顯示binFile源代碼,接著上次的位置往下列,每次列10行。
list/l 函數(shù)名:列出某個(gè)函數(shù)的源代碼
break(b) 行號(hào):在某一行設(shè)置斷點(diǎn)
info break :查看斷點(diǎn)信息
break 函數(shù)名:在某個(gè)函數(shù)開頭設(shè)置斷點(diǎn)
d + 斷點(diǎn)編號(hào):刪除斷點(diǎn)
delete breakpoints:刪除所有斷點(diǎn)
r或run:運(yùn)行程序
s或step:進(jìn)入函數(shù)調(diào)用
進(jìn)入了AddToVal函數(shù)內(nèi)部,遇到斷點(diǎn)停下
n 或 next:單條執(zhí)行
每個(gè)n執(zhí)行一行
print§:打印表達(dá)式的值,通過(guò)表達(dá)式可以修改變量的值或者調(diào)用函數(shù)
display 變量名:跟蹤查看一個(gè)變量,每次停下來(lái)都顯示它的值
undisplay:取消對(duì)先前設(shè)置的那些變量的跟蹤
finish/fin:執(zhí)行到當(dāng)前函數(shù)返回,然后停下來(lái)等待命令
3.總結(jié)
到此,我們學(xué)習(xí)了Linux下的基本指令與操作,權(quán)限相關(guān)的概念,學(xué)習(xí)了yum工具,能夠在Linux下進(jìn)行軟件的安裝,學(xué)習(xí)了vim的使用,能夠在Linux下寫代碼,學(xué)習(xí)了gcc/g++的使用,能夠在Linux下編譯代碼,學(xué)習(xí)了gdb的使用,能夠在Linux下調(diào)試代碼,學(xué)習(xí)了make/makefile,能夠在Linux下使用多文件編程,為我們?cè)贚inux下編程提供了便利,編寫了我們的第一條Linux程序----進(jìn)度條,學(xué)會(huì)了使用git命令行,能夠把Linux下的代碼上傳到Gitee/Github上。Linux的工具篇到此結(jié)束,下面,我們將會(huì)遇到Linux的第一座大山----進(jìn)程。
本章完