gcc编译以及Makefile与GDB调试
一:編譯選項(xiàng): ? ? ?
gcc常用編譯的選項(xiàng):
-c 表示編譯源文件,只編譯并生成目標(biāo)文件。
-E 只運(yùn)行 C 預(yù)編譯器。
-o 表示輸出目標(biāo)文件
-g 表示在目標(biāo)文件中產(chǎn)生調(diào)試信息, 用于 gdb 調(diào)試
-D<宏定義> 編譯時(shí)將宏定義傳入進(jìn)去
-Wall 打開(kāi)所有類(lèi)型的警告。
-w 不生成任何警告信息。
-ansi 只支持 ANSI 標(biāo)準(zhǔn)的 C 語(yǔ)法。這一選項(xiàng)將禁止 GNU C 的某些特色,
1、gcc 編譯過(guò)程: 預(yù)編譯--編譯--匯編--鏈接
? ? ???????????????????????????????????????????????????????????????? ??????????????
2、編譯過(guò)程包括下面幾個(gè)階段:
(1)預(yù)處理: 預(yù)處理器將對(duì)源文件中的宏進(jìn)行展開(kāi)。
(2)編譯: gcc 將 c 文件編譯成 匯編文件。
(3)匯編: as 將匯編文件編譯成機(jī)器碼。
(4)鏈接: 將目標(biāo)文件和外部符號(hào)進(jìn)行連接, 得到一個(gè)可執(zhí)行二進(jìn)制文
件。
?
3、gcc 編譯常用選項(xiàng)
?
4、一般編譯過(guò)程如下:
預(yù)處理階段: 對(duì)包含的頭文件( #include) 和宏定義( #define、 #ifdef等) ? ? ? ? ? ? ??
? ? ? ? ? ? 進(jìn)行處理
? ? ? ? ? ? gcc ?–E ?hello.c ?–o ?hello.i
//-o 表示輸出為指定文件類(lèi)型 -E 將源文件(*.c) 轉(zhuǎn)換為(*.i)
編譯階段: 檢查代碼規(guī)范性、 語(yǔ)法錯(cuò)誤等, 在檢查無(wú)誤后把代碼翻譯成匯編語(yǔ)言
? ? ? ? ? ? gcc –S hello.i –o hello.s
? ? ? ? ? ? //-S 將已預(yù)處理的 C 原始程序(*.i) 轉(zhuǎn)換為(*.s)
鏈接階段: 將.s 的文件以及庫(kù)文件整合起來(lái)鏈接為可執(zhí)行程序
? ? ? ? ? ??gcc? hello.s?–o?hello.exe(或hello)
? ? ? ? ? ? //最后將匯編語(yǔ)言原始程序(*.s)和一些庫(kù)函數(shù)整合成( *.exe)
5、條件編譯
#include <stdio.h>main(){#ifdef cjy//表示如果定義了 cjy, 即命令行參數(shù)傳了cjy, 就執(zhí)行下面的輸出printf("cjy is defined!\n");#elseprintf("cjy is not defined!\n");#endifprintf("main exit\n");}注:gcc –E project2.c –o project2.i –D cjy? ? ? //條件編譯, 用-D 傳遞,
? ? ? ?或: gcc ? project2.c ?–o ?project2 ?–D ?cjy ? ? ? ?
? ? ? ?如果沒(méi)有傳 cjy 則執(zhí)行#else
? ? ? ?gcc ?–S ? project2.i ?–o ?project2.s
? ? ? ?gcc ? project2.s ? ? ??–o ?project2
6、gcc 編譯庫(kù)選項(xiàng)
? ? ??
二、函數(shù)庫(kù)(靜態(tài)庫(kù)以及動(dòng)態(tài)庫(kù)簡(jiǎn)述)
1、簡(jiǎn)述
靜態(tài)庫(kù)是目標(biāo)文件.a 的歸檔文件( 格式為 libname.a)。
如果在編譯某個(gè)程序時(shí)鏈接靜態(tài)庫(kù), 則鏈接器將會(huì)搜索靜態(tài)庫(kù)并直接拷貝到該程序
的可執(zhí)行二進(jìn)制文件到當(dāng)前文件中;
動(dòng)態(tài)庫(kù)( 格式為 libname.so[.主版本號(hào).次版本號(hào).發(fā)行號(hào)])。
在程序編譯時(shí)并不會(huì)被鏈接到目標(biāo)代碼中, 而是在程序運(yùn)行時(shí)才被載入。
2、創(chuàng)建靜態(tài)庫(kù)及動(dòng)態(tài)庫(kù)
靜態(tài)庫(kù):
gcc -c add.c ? ?//編譯 add.c 源文件生成 add.o 目標(biāo)文件ar ?crsv ?libadd.a ?add.o ?//對(duì)目標(biāo)文件*.o 進(jìn)行歸檔, 生成 lib*.a,此處 lib 必須要寫(xiě)的
然后就是在編譯其他文件時(shí),鏈接靜態(tài)庫(kù)。
gcc ?-o mian ?main.c ?-L./ ?–ladd –I./
//不要忘記-L 后面的那個(gè). (即在庫(kù)文件的搜索路徑中添加當(dāng)前路徑 -ladd 表示鏈接庫(kù)文件 libadd.a/.so ? ? -I./表示包含在當(dāng)前目錄中的頭文件)
如果我們直接將編譯完成的.o文件直接加入到了庫(kù)的末尾,卻并沒(méi)有更新庫(kù)的有效符號(hào)表。連接程序進(jìn)行連接時(shí),在靜態(tài)庫(kù)的符號(hào)索引表中無(wú)法定 位剛才加入的.o文件中定義的函數(shù)或者變量。這就需要在完成庫(kù)成員追加以后讓加入的所有.o文件中定義的函數(shù)(變量)有效,完成這個(gè)工作需要使用另外一個(gè) 工具“ranlib”來(lái)對(duì)靜態(tài)庫(kù)的符號(hào)索引表進(jìn)行更新。
GNU工具中ar是用來(lái)制作庫(kù)文件.a的,但同時(shí)還提供了一個(gè)ranlib,從手冊(cè)上看ranlib相當(dāng)于ar -s,為什么這樣呢?
這是由于最早在Unix系統(tǒng)上ar程序是單純用來(lái)打包多個(gè).o到.a(類(lèi)似于tar做的事情),而不處理.o里的符號(hào)表。Linker程序則需 要.a文件提供一個(gè)完整的符號(hào)表,所以當(dāng)時(shí)就寫(xiě)了單獨(dú)的ranlib程序用來(lái)產(chǎn)生linker所需要的符號(hào)信息。也就是說(shuō),產(chǎn)生一個(gè)對(duì)linker合 格的的.a文件需要做ar和ranlib兩步 。
動(dòng)態(tài)庫(kù):
gcc -fPIC -Wall -c add.cgcc -shared -o libadd.so add.o最后編譯鏈接動(dòng)態(tài)庫(kù): gcc -o main main.c -L. –ladd
在運(yùn)行 main 前, 需要注冊(cè)動(dòng)態(tài)庫(kù)的路徑。常采用的方法有:
cp ?libadd.so ?/lib ? //通常采用的方法, cp ?lib*.so /lib ?將其copy到系統(tǒng)的/lib文件夾下
創(chuàng)建動(dòng)態(tài)鏈接庫(kù)之后, 以后就可以使用該動(dòng)態(tài)鏈接庫(kù)了
例如在 test.c 里面調(diào)用了原來(lái)庫(kù)中的函數(shù), 則執(zhí)行 gcc ?test.c?–o test ? ?–ladd?就可以了。較方便
3、靜態(tài)庫(kù)與動(dòng)態(tài)庫(kù)的比較
動(dòng)態(tài)庫(kù)只在執(zhí)行時(shí)才被鏈接使用, 不是直接編譯為可執(zhí)行文件, 并且一個(gè)動(dòng)態(tài)庫(kù)可以被多個(gè)程序使用故可稱為共享庫(kù)。
靜態(tài)庫(kù)將會(huì)整合到程序中,在程序執(zhí)行時(shí)不用加載靜態(tài)庫(kù)。因此,靜態(tài)庫(kù)會(huì)使你的程序臃腫并且難以升級(jí),但比較容易部署。而動(dòng)態(tài)庫(kù)使你的程序輕便易于升級(jí)但難以部署。
4、gcc --- 優(yōu)化選項(xiàng)
gcc 對(duì)代碼進(jìn)行優(yōu)化通過(guò)選項(xiàng)“-On”來(lái)控制優(yōu)化級(jí)別(n是整數(shù))。
優(yōu)化選項(xiàng)“-O1”:主要進(jìn)行線程跳轉(zhuǎn)和延遲退棧兩種優(yōu)化。
選項(xiàng)“-O2” 除了完成所有“ -O1” 級(jí)別的優(yōu)化之外,還要進(jìn)行一些額外的調(diào)整工作,如處理其指令調(diào)度等。
“-O3” 則還包括循環(huán)展開(kāi)或其他一些與處理器特性相關(guān)的優(yōu)化工作。
優(yōu)化目標(biāo):
- -O,-O1:效果是一樣的,目的都是在不影響編譯速度的前提下,盡量采用一些優(yōu)化算法降低代碼大小和可執(zhí)行代碼的運(yùn)行速度。它主要對(duì)代碼的分支,常量以及表達(dá)式等進(jìn)行優(yōu)化。
- -O2 :該優(yōu)化選項(xiàng)會(huì)犧牲部分編譯速度,除了執(zhí)行 -O1 所執(zhí)行的所有優(yōu)化之外,還會(huì)采用幾乎所有的目標(biāo)配置支持的優(yōu)化算法,用以提高目標(biāo)代碼的運(yùn)行速度。會(huì)嘗試更多的寄存器級(jí)的優(yōu)化以及指令級(jí)的優(yōu)化,它會(huì)在編譯期間占用更多的內(nèi)存和編譯時(shí)間。
-
-O3 :該選項(xiàng)除了執(zhí)行-O2所有的優(yōu)化選項(xiàng)之外,一般都是采取很多向量化算法,提高代碼的并行執(zhí)行程度,利用現(xiàn)代CPU中的流水線,Cache等。例如使用偽寄存器網(wǎng)絡(luò),普通函數(shù)的內(nèi)聯(lián),以及針對(duì)循環(huán)的更多優(yōu)化
-
-Os :O3的目標(biāo)是寧愿增加目標(biāo)代碼的大小,也要拼命的提高運(yùn)行速度,但是這個(gè)選項(xiàng)是在-O2的基礎(chǔ)之上,盡量的降低目標(biāo)代碼的大小,這對(duì)于存儲(chǔ)容量很小的設(shè)備來(lái)說(shuō)非常重要。一般就是壓縮內(nèi)存中的對(duì)齊空白(alignment padding)。主要是對(duì)代碼大小的優(yōu)化,
-
-Og : 該標(biāo)識(shí)會(huì)精心挑選部分與-g選項(xiàng)不沖突的優(yōu)化選項(xiàng),當(dāng)然就能提供合理的優(yōu)化水平,同時(shí)產(chǎn)生較好的可調(diào)試信息和對(duì)語(yǔ)言標(biāo)準(zhǔn)的遵循程度。
注意:雖然優(yōu)化選項(xiàng)可以加速代碼的運(yùn)行速度,但對(duì)于調(diào)試而言將是一個(gè)很大的挑戰(zhàn),因?yàn)榇a在經(jīng)過(guò)優(yōu)化之后,原先在源程序中聲明和使用的變量很可能不再使用,控制流也可能會(huì)突然跳轉(zhuǎn)到意外的地方, 循環(huán)語(yǔ)句也有可能因?yàn)檠h(huán)展開(kāi)而變得到處都有,所有這些對(duì)調(diào)試來(lái)講都是不好的。所以在調(diào)試的時(shí)候最好不要使用任何的優(yōu)化選項(xiàng),只有當(dāng)程序在最終發(fā)行的時(shí)候才考慮對(duì)其進(jìn)行優(yōu)化。
三、make工程管理器
1、簡(jiǎn)介
工程管理器指管理較多的文件,它是自動(dòng)管理器能根據(jù)文件時(shí)間自動(dòng)發(fā)現(xiàn)更新過(guò)的文件而減少編譯的工作量,同時(shí)通過(guò)讀入Makefile文件來(lái)執(zhí)行大量的編譯工作。(自動(dòng)化)
2、Makefile基本語(yǔ)法及格式
target: dependency_files //目標(biāo)項(xiàng):依賴項(xiàng)
< TAB >command ? ? ? ? ? //必須以 tab 開(kāi)頭, command為編譯命令
編寫(xiě)好Makefile之后,就可以直接使用 ?make 編譯了。若寫(xiě)了clean,可make clean 清理,重新編譯。
如下:
? ?????????????????? ? ? ? ? ??
特殊處理與偽目標(biāo):
.PHONY 是 Makefile 文件的關(guān)鍵字, 表示它后面列表中的目標(biāo)均為偽目標(biāo)。偽目標(biāo)通常用在清理文件、 強(qiáng)制重新編譯等情況下。
eg:??????
.PHONY:clean
clean:
? ? ?rm main ?main.o
變量、 函數(shù)與規(guī)則
1、引言
? ? 隨著軟件項(xiàng)目的變大、變復(fù)雜,源文件也越來(lái)越多,如果采用前面的方式makefile 文件,將會(huì)使makefile也變得復(fù)雜而難于維護(hù)。故通過(guò)make支持的變量定義、規(guī)則和內(nèi)置函數(shù),可以寫(xiě)出通用性較強(qiáng)的makefile文件,使得同一個(gè)makefile文件能夠適應(yīng)不能的項(xiàng)目是重要的。
2、變量: 用來(lái)代替一個(gè)文本字符串
? ? 變量名:=變量值 ? ? 簡(jiǎn)單變量展開(kāi)(類(lèi)似于 C++的賦值) ? //通常采用這種形式
使用變量的一般方法: $(變量名)=??? 賦值 ? ? ? ????=$(變量名) 引用
例如:--下
? ? ? ? ? ??????? ? ?
變量分為: 用戶自定義變量, 預(yù)定義變量( CFLAGS), 自動(dòng)變量, 環(huán)境變量
自動(dòng)變量: 指在使用的時(shí)候, 自動(dòng)用特定的值替換。
常見(jiàn)的有: $@ 當(dāng)前規(guī)則的目標(biāo)文件(重點(diǎn))?
? ? ? ? ? $^ 當(dāng)前規(guī)則的所有依賴文件, 以空格分隔(重點(diǎn))
? ? ? ? ? eg:用自動(dòng)變量 ? ? ?CFLAGS:= -Wall -O2 –fpic
預(yù)定義變量: 內(nèi)部事先定義好的變量, 但是它的值是固定的, 并且有
些的值是為空的。
AR: 庫(kù)文件打包程序默認(rèn)為 ar ? ? ? ? ? ? CC: c 編譯器默認(rèn)為 cc
CPP: c 預(yù)編譯器, 默認(rèn)為$(CC) –E ? ? ? ?CFLAGS: c 編譯器選項(xiàng), 無(wú)默認(rèn)
CXXFLAGS: c++編譯器選項(xiàng)
? ? ? ? ? ? ? ??
規(guī)則分為: 普通規(guī)則, 隱含規(guī)則, 模式規(guī)則
隱含規(guī)則:? //*.o 文件自動(dòng)依賴*.c 或*.cc 文件, 所以可以
省略main.o:main.cpp 等
模式規(guī)則: 通過(guò)匹配模式找字符串, %匹配 1 或多個(gè)任意字符串 ? ?%.o: %.cpp 任何目標(biāo)文件的依賴文件是與目標(biāo)文件同名的并且擴(kuò)展名為.cpp 的文件
函數(shù):
1. wildcard 搜索當(dāng)前目錄下的文件名, 展開(kāi)成一列所有符合由其參數(shù)描述的文件名,文件間以空格間隔。 SOURCES = $(wildcard *.cpp)把當(dāng)前目錄下所有'.cpp'文件存入變量 SOURCES 里。
2、 字符串替換函數(shù):(patsubst 要查找的子串,替換后的目標(biāo)子串, 源字符串)。 將源字符串(以空格分隔)中的所有要查找的子串替換成目標(biāo)子串。 如
OBJS = $(patsubst %.cpp,%.o,$(SOURCES)) ? 可以和上一步對(duì)應(yīng)起來(lái)看。即把 SOURCES 中'.cpp' 替換為'.o' 。
3、(addprefix 前綴, 源字符串)函數(shù)把第二個(gè)參數(shù)列表的每一項(xiàng)前綴加上第一個(gè)參 ? ? ??
?? 數(shù)值
3、如下為一個(gè)較為通用的Makefile:
SOURCES:=$(wildcard *.c)
OBJS:=$(patsubst %.c,%.o,$(SOURCES))
ELF:=main
CC:=gcc
CFLAGS:=-g -Wall
$(ELF):$(OBJS)
? ? ? ? gcc $^ -o $@
.PHONY:clean
clean:
? ? ? ? rm $(OBJS) $(ELF)
四、Gdb程序調(diào)試
1、gdb 常用命令
- 首先程序編譯時(shí)加?-g 選項(xiàng)才可打開(kāi)調(diào)試選項(xiàng)?
- eg:gcc –o filename –Wall filename.c –g //進(jìn)入調(diào)試
- gdb filename //進(jìn)入調(diào)試
- l ? ?//顯示代碼 (list)
- b ?4 ? ?//在第四行設(shè)置斷點(diǎn) 相當(dāng)于 Windows 的 F9?(break) ? ? ? ? ??//若為 b ?main ?則表示斷點(diǎn)打在main處
- r ? ?//運(yùn)行 ? 相當(dāng)于 Windows 的 F5 (run)
- n //下一步不進(jìn)入函數(shù) 相當(dāng)于 Windows 的 F10 ?(next)
- s //表示單步進(jìn)入函數(shù), 相當(dāng)于 Windows 的 F11 (step)
- p ?I ?//打印變量 I 相當(dāng)于 Windows 的 Watch 窗口(print)
- c ? ? //運(yùn)行到最后(continue)
- bt? ?// 用于查看當(dāng)前堆棧
- f? ?4? ? // 進(jìn)入第 4 層堆棧?
- q ? ? //退出 相當(dāng)于 Windows 的 ? Shift+F5 (quit)
??????????
五、其他補(bǔ)充
1. 在 linux 中利用 system(“clear”);實(shí)現(xiàn)類(lèi)似于 windows 里面的清屏函數(shù) system(“cls”);
2. LINUX 中可以通過(guò)下面的方式可以實(shí)現(xiàn) system("pause");功能:
printf(“Press any key to continue…”);getchar();getchar(); //要用兩個(gè) getchar()函數(shù)3. linux 中如何刷新輸入緩沖區(qū), 利用 getchar()函數(shù)即可。 輸出緩沖區(qū)可以利用 fflush(stdout);
4.命令 x 是用來(lái)檢查內(nèi)存情況, 英文是 examine 含義, 使用方法? ?x /20xb 變量首地址, 其中 20x 代表 16 進(jìn)制的長(zhǎng)度, b 代表字節(jié)的含義
5.針對(duì)段錯(cuò)誤, 可以通過(guò) ulimit -c unlimited 設(shè)置 core file size 為不限制大小, 設(shè)置完畢后, 可以通過(guò) ulimit -a 進(jìn)行查看是否設(shè)置 ok, 這時(shí)候再次運(yùn)行程序,會(huì)產(chǎn)生 core 文件,通過(guò) gdb 可執(zhí)行程序 core 文件,
進(jìn)行調(diào)試。 直接通過(guò) bt 可以看到程序段錯(cuò)誤時(shí)的現(xiàn)場(chǎng), 通過(guò) f? 1 可以直接切換到程序現(xiàn)場(chǎng)(第一層堆棧)。
gdb ./test2 core
6.調(diào)試正在運(yùn)行的程序, 通過(guò) attach 進(jìn)程 ID, 調(diào)試正在運(yùn)行的程序
其他:更多操作可以參考:?1. gdb 調(diào)試?yán)?— Linux Tools Quick Tutorial
GDB 優(yōu)秀博文:?Linux基礎(chǔ) 30分鐘GDB調(diào)試快速突破 - 喜歡蘭花山丘 - 博客園
總結(jié)
以上是生活随笔為你收集整理的gcc编译以及Makefile与GDB调试的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Java并发编程,Condition的a
- 下一篇: React Suspense提供Redu