我們習慣用gcc 編譯C語言程式碼,g++編譯C++的程式碼
gcc也可以編譯Go語言程式之類的,gcc可以根據檔案字尾來判斷當前程式所用的語言
以test.c為例:
gcc -E test.c -o test.i 預處理:完成宏替換,去掉註釋,標頭檔案包含等工作
gcc -S test.i -o test.s 編譯: 把C語言程式碼轉化成組合語言的程式碼
gcc -c test.s -o test.o 組合:把組合語言的程式碼轉成機器語言(二進位制序列)
gcc test.o -o mytest 連結:生成可執行程式(預設是動態連結)
下面對每步進行演示
📖①**vim test.c**
📖②gcc -E test.c -o test.i
-E 作用:預處理,但不生成檔案
-o的作用:把生成的內容輸出到檔案,此外有給檔案起名的作用
gcc -E test.c -o test.i 裡的-o就是把預處理的內容輸出到test.i裡,不然直接列印到螢幕上了
cat -n test.i
test.i的內容
📖③gcc -S test.i -o test.s
-S:編譯 ,不生成檔案
-o 把生成的內容放入test.s
cat -n test.s
生成的組合程式碼如下:
📖④gcc -c test.s -o test.o
-c 組合不生成檔案
-o 把生成的內容放入test.o
cat -n test.o
看不懂對吧,我也看不懂,可以轉為二進位制檔案看xxd test.o
看不懂對吧 我也看不懂🐱🏍📖⑤gcc test.o -o mytest
這一步寫成 gcc test.o -o mytest.exe 效果一樣,Linux不是根據擴充套件名來給檔案歸類的,反正都是可執行檔案,具有x許可權
gcc test.o :連結 生成可執行檔案.
mytest檔案就不看了,和上面的test.o差不多,也是二進位制檔案連結:分為動態連結和靜態連結,預設是動態連結
ldd和file可以判斷連結型別
📖 ldd mytest
.so可以判斷動態連結,.a判斷是靜態連結
libc.so.6就是個動態庫,libc.a就表示是個靜態庫
📖file mytest
📖 英文時間:dynamically linked==動態連結
既然有動態連結 肯定就有靜態連結 兩者區別又是什麼?
動態連結用到的是靜態庫,執行時載入庫。
靜態連結則是直接包含庫,所以靜態連結生成的檔案會很大
怎麼生成靜態連結的可執行檔案?
加-static
舉例:gcc test.c -o mytest-s -static(如果報錯了拉到下一個塊解決報錯)
生成的mytest-s就是靜態連結的,看看具體資訊
file mytest-s
📖 英文時間:statically linked==靜態連結
📖 再比較下動態和靜態庫的大小
靜態連結生成的檔案大小是動態連結生成的100倍左右
不過靜態連結也有優點,沒有那麼依賴設定環境,拿過來就能用。
不加-static預設是動態連結
報錯資訊:
/usr/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
原因:少了靜態庫…搞了半天才解決
解決: sudo yum install glibc-static
把庫給裝上就完事了
補充:gcc -w 不生成任何警告
gcc -Wall 生成所有警告
我們預設是報警告的,所有預設是gcc -Wall
test.cpp test.cc test.cxx都表示c++檔案
gcc編譯器可以根據檔案字尾判斷檔案的程式語言
英文時間: pwd==print work directory
什麼是gdb?
簡單來說就是偵錯程式,比如vs裡的偵錯程式
gdb 可執行程式
作用:進入偵錯
如gdb mytest
**如果報錯:**重新生成可執行檔案,gcc命令加上-g,不加預設release版本
例 gcc mytest -g
報錯原因:檔案裡沒有偵錯資訊,也就是我們常說的release版本
gdb介面輸入命令報錯資訊:No symbol table is loaded. Use the 「file」 command.
-g可加入偵錯資訊 ,release版本無偵錯資訊,不可被偵錯
檢查當前檔案是否具有偵錯資訊
readelf -S mytest |grep debug
效果如圖:(不列印則說明沒有debug資訊)
📖 小知識:為什麼會有debug和release?
debug是給我們開發時用的,release一般是給使用者用的
偵錯資訊也佔空間啊
如果只有debug版本,使用者又不用偵錯,偵錯資訊又多佔了程式空間
所以對使用者來說對debug的需求不強(沒有)
如果只有release版本,自己感受一下出了bug沒有偵錯程式的感覺[doge]
📖 quit(q)或者ctrl+d:退出gdb
l :顯示十行程式碼 按回車顯示十行後的程式碼
但不一定是開頭十行
📖 list (l) 行號:顯示行號所在的程式碼
📖 l 函數名 :顯示函數的程式碼
📖 b 行號:給某行加斷點 (b== Breakpoint )
如 b 7
📖 b 函數名:在函數入口加斷點
📖 info b:列出所有斷點
📖 r :開始偵錯 兩次r重新偵錯 r ==run
📖 n : 逐過程(不會進入函數) n ==next
📖 p 變數:列印變數的值,但只顯示一次
📖 print 表示式:列印表示式的值(變數也算入表示式)
📖 display 變數:長顯示,每次n都會列印
比如display sum 長顯示sum,每次n都會顯示sum的值
📖 undisplay 編號 取消長顯示 注意是編號不是變數名
📖 c (continue): 跳到下一個斷點
📖 d 編號 :刪除斷點
📖 s 逐語句 :會進入函數
📖 bt :顯示呼叫堆疊
📖 until 行號 跳轉到函數裡的任意位置
和斷點有一些衝突
迴圈體:
for( i=0;i<=top;i++) { (斷點) sum+=i;//斷點啟用時(走到這行了用until) until未能跳出迴圈 }
📖 finish 跑完當前函數 (官方點:執行到當前函數返回,然後停下來等待命令 非main函數)
📖 c until finish :跳轉三劍客了屬於是 🗡
Enb=enable y表示斷點可用
📖 disable 斷點編號 :禁用斷點
為啥要禁用斷點呢,我不用直接刪掉不就行了
答:保留偵錯痕跡,方便多次偵錯
📖 delete breakpoint 、delete breakpoints :刪掉全部斷點
set var 變數:改變偵錯時變數的值
比如set var i=100,用的比較少
注:改變變數的值不是VS裡的條件斷點,而是直接改變往後執行
make:這是一條指令
makefile/Makefile:這是一個檔案
依賴關係
test.exe的生成依賴於test.c
📖 touch makefile/touch Makefile
makefile裡面寫的是依賴關係和相應的操作
mytest:test.o 表示mytest依賴於test.o生成,表現出依賴關係
gcc test.o -o mytest gcc前面必須是Tab 不能是四個空格
test.o:test.s 表示test.o依賴於test.s生成,表現出依賴關係
gcc -c test.s -o test.o
test.s:test.i 表示test.s依賴於test.i生成,表現出依賴關係
gcc -S test.i -o test.s
test.i:test.c 表示test.i依賴於test.c生成,表現出依賴關係
gcc -E test.c -o test.i
📖 解釋一下這個make吧 ,相當於一次性執行了多條命令,降低了我們的偵錯的成本
如果檔案很多 寫gcc一個一個編譯連結過去成本太高
或者是軟體迭代的時候,進行測試等操作時一個一個寫gcc成本太高了
專案管理有生成檔案的,自然就有刪除檔案的
📖 刪除檔案時makefile這麼寫:
📖 .PHONY:clean又是什麼東西???
.PHONY很像一個宣告,宣告一下clean,或者說修飾一下clean
.PHONY定義偽目標,被修飾的clean總是被執行
⌨ 總是被執行?那肯定就有總是不被執行了
預設:總是不被執行
下面的make就是總是不被執行,即只能被執行一次,也一直提示make: `mytest’ is up to date.
同時對比clean
看到這還有一個問題
📖 為什麼生成檔案時只需要輸入「make」而我在刪除檔案時卻要輸入「make clean」?
因為make就是從makefile檔案由上往下走,執行第一條命令
這麼說很晦澀,舉個例子
全稱就應該寫 make mytest,只不過mytest相關命令寫在clean前面,所以可以簡寫為make
如果把clean寫在第一行,那make命令執行的就是刪除功能了,有興趣可以試試
此外make命令不會檢查檔案是否存在,找不到操作的檔案就直接報錯退出了
1 mytest:test.o
2 gcc $^ -o $@ $@依賴於$^,類似於代指
3 test.o:test.s
4 gcc -c $^ -o $@
5 test.s:test.i
6 gcc -S $^ -o $@
7 test.i:test.c
8 gcc -E $^ -o $@
如果有幫助的話麻煩點個贊鼓勵一下!