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