1.編譯過程 1.1 預處理(Pre-Processing) 展開頭文件, 巨集替換(變數巨集、函數巨集)、替換空格等 gcc -E hello.c -o hello.i // -E 預處理選項, -o 重命名 1.2 編譯(Compilation) 逐行檢查程式中出現的語法錯誤,簡單的邏輯錯誤 gcc ...
1.編譯過程
1.1 預處理(Pre-Processing)
展開頭文件, 巨集替換(變數巨集、函數巨集)、替換空格等
gcc -E hello.c -o hello.i // -E 預處理選項, -o 重命名
1.2 編譯(Compilation)
逐行檢查程式中出現的語法錯誤,簡單的邏輯錯誤
gcc -S hello.i -o hello.s
1.3 彙編(Assemble)
將 .s 彙編文件中所有的彙編指令翻譯成二進位機器碼(下麵就是來了個截圖,二進位顯示了亂碼)
gcc -c hello.s -o hello.o
1.4 鏈接(Linking)
將 .o 的目標文件,鏈接庫文件、數據段合併,地址回填(把彙編里相對地址替換成程式運行後真正可以運行的地址)。生成可執行文件 hello
gcc hello.o -o hello
1.5 gcc常用參數
-E // 展開頭文件 -s // 生成彙編文件 -c // 編譯生成2進位文件 -I // 指定頭文件路徑 大i -L // 指定庫文件路徑 -l // 指定庫名 小L -g // 包含調試信息 -On // n取 0-3, n 越大優化級別越高 -Wall //warning all 顯示所有警告 -D // 編譯時動態註入一個巨集, 編譯的時候控制#define的具體數值
2.動態庫和靜態庫
2.1 函數庫
本質:把具有相同功能的一組函數放到同一份文件中(源碼或二進位的形式)
2.2 靜態庫
對執行速度有要求
- 機制:把引入的庫文件通過編譯直接複製到 .out 文件中
- 優點:將函數庫中的函數本地化,定址方便,速度快(函數庫執行效率 = 自定義函數的執行效率)
- 缺點:每個程式都需要複製一份,會浪費記憶體
製作靜態庫:
gcc -c add.c sub.c mul.c // 製作 .o 文件 ar rs libmymath.a add.o sub.o mul.o // 製作靜態庫 gcc hello.c -L ./ -lmymath -o app // 指定頭文件路徑, 指定庫名, 設置文件輸出名app
2.3 動態庫
對執行速度不敏感,對系統資源敏感;更新比較頻繁(可以避免完全重新編譯)
- 機制:代碼共用
- 優點:節省記憶體(共用)、易於更新(動態鏈接)
- 缺點:相較於靜態庫函數調用速度慢,函數地址是延時綁定機制
gcc -c -fPIC add.c sub.c mul.c // -fPIC生成與位置無關的目標文件 gcc -shared -o libmymath.so sub.o mul.o add.o // 生成一個動態庫 gcc hello.c -o app -L ./lib -l mymath -I ./inc // 生成文件
啟動 ./app 報錯,錯誤原因:”動態鏈接器“ ld-linux-x86-64.so.2 搜索動態庫的路徑沒有指定
鏈接器:工作於 gcc 編譯過程中的鏈接階段。工作結束後生成可執行文件
動態鏈接器:工作於可執行程式運行之後,輔助載入器負責將動態庫載入到記憶體
解決辦法:
環境變數法:export LD_LIBRARY_PATH=./lib 將當前動態庫所在的目錄加入到環境變數,終端退出後環境變數就會失效
配置文件法:vi .bashrc 加入一行 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./lib,重啟終端,每次啟動終端自動生效
拷貝法:把自己的動態庫直接拷貝到 /lib 或 /usr/lib 中
緩存文件法(推薦):修改配置文件,修改緩存文件,生成動態鏈接器需要搜索的新目錄位置
sudo vim /etc/ld.so.conf // 修改動態鏈接庫的路徑 // 將絕對路徑增加到文件中 sudo ldconfig -v // 更新 ld.so.cache 該文件影響動態鏈接庫搜索的位置
3.makefile
3.1 語法
目標:依賴條件
tab縮進,指令
hello:hello.o add.o sub.o mul.o gcc hello.o add.o sub.o mul.o -o hello hello.o:hello.c gcc -c hello.c -o hello.o sub.o:sub.c gcc -c sub.c -o sub.o add.o:add.c gcc -c add.c -o add.o mul.o:mul.c gcc -c mul.c -o mul.o
模式規則:在makefile中,具有有嚴格統一的規則,使用模式規則代替,模式規則中只能使用 $< 符號
%.o:%.c
gcc -c $< -o $@
靜態模式規則:將模式規則指定給某一個變數使用
$(obj):%.o:%.c
gcc -c $< -o $@
偽目標:針對殘缺的規則,使之生成目標(比如有一個clean.o文件已經是最新的,就不會執行clean清除命令
.PHONY:clean ALL
3.2 函數介紹
3.2.1 wildcard 函數
// 匹配當前工作目錄下的所有 .c 文件,將文件名組成列表,賦值給 src 變數 // src = add.c sub.c mul.c hello.o src = $(wildcard ./*.c)
3.2.2 patsubst 函數
// 用來替換參數, 將 參數3 中包含 參數1 的部分替換為 參數2 // obj = add.o sub.o mul.o hello.o obj = $(patsubst %.c, %.o, $(src))
3.3 普通變數和自動變數
普通變數
- 定義變數語法:變數名 = 變數值 (foo = abc)
- 取變數值語法:$(變數) (bar = $abc ---> bar = abc)
自動變數
- $@: 在規則的命令中,表示規則中的目標
- $^ : 在規則的命令中,表示所有依賴條件
- $< : 在規則的命令中,表示第一個依賴條件。如果該變數應用在”模式規則“中,它可以將依賴條件列表中的每一個依賴,依次取出,套用模式規則
3.4 關鍵字
ALL: 預設情況下第一組生成文件的目標就是終極目標,或者顯示的寫ALL:hello表示終極目標,完成後結束makefile
clean: 藉助makefile清除項目中的指定文件,例如(clean: -rm -rf $(obj) hello
指令 make -n 模擬執行命令,不真正執行,可以先看一次避免出問題
3.5 修改後的 makefile
src = $(wildcard *.c) obj = $(patsubst %.c, %.o, $(src)) CC = gcc target = app ALL:$(target) $(target):$(obj) $(CC) $^ -o $@ $(obj):%.o %.c $(CC) -c $< -o $@ clean: -rm -rf $(obj) $(target) .PHONY:clean ALL
4. gdb調試
4.1 基礎指令
基礎的斷點設置和繼續運行等指令
- -g:使用該指令編譯可執行文件,否則沒有調試表
- gdb ./a.out
- list:list 1 列出源碼,根據源碼指定行號設置斷點,1表示從第一行開始顯示源碼
- b:b 55 在第55行添加斷點
- run/r:運行程式,啟動調試(可以直接找到停止位置就是出錯位置)
- next/n:下一條指令(越過函數)
- step/s:下一條執行(進入函數內部)
- print/p:列印變數值,如p var 查看 var 變數的值
- continue:執行到下一個斷點
- finish:結束當前函數調用
- quit:退出調試
- start:不使用斷點,直接開始單步調試
設置一些條件和查看棧幀及變數
- set args:啟動gdb調試後,通過該指令可以設置main函數的參數,需要在start和run指令之前設置
- info b:查看當前斷點信息表
- b 23 if i=5:設置斷點在23行,如果 i=5 時斷點才生效
- ptybe:查看變數類型
- display:設置跟蹤變數,display i,跟蹤變數 i
- undisplay:取消跟蹤變數
- bt:列出當前程式存活的棧幀
- frame:如果有多層調用,在內層想查看外層棧幀里的參數,使用 frame n (n 是棧幀編號)切換棧幀,再使用 p var 列印變數