首先就是要熟練在vim里面寫代碼,其實就是沒有提示和自動補全了,這個問題並不大。
我服務器gcc版本是4.8.5,所以就按照這個來了 https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/
其實我的開發者環境是最新的9.1.0,非常不建議哦。生產環境和開發環境盡量相同,不同的話一定要進行大量的測試
然后就是編譯,先cd到工程文件夾,然后使用編譯命令編譯
一、編譯
編譯:當前源代碼編譯成二進制目標文件(.obj文件)
鏈接(link):將生成的.obj文件與庫文件.lib等文件鏈接,生成可執行文件
一個現代編譯器的主要工作流程如下:
源程序(source code)→預處理器(preprocessor)→編譯器(compiler)→匯編程序(assembler)→目標程序(object code)→連接器(鏈接器,Linker)→可執行程序(executables)
執行過程 雖然我們稱gcc是C語言的編譯器,但使用gcc由C語言源代碼文件生成可執行文件的過程不僅僅是編譯的過程,而是要經歷四個相互關聯的步驟∶
1.預處理(也稱預編譯,Preprocessing):命令gcc首先調用cpp進行預處理,在預處理過程中,對源代碼文件中的文件包含(include)、預編譯語句(如宏定義define等)進行分析。
2.編譯(Compilation):接着調用cc1進行編譯,這個階段根據輸入文件生成以.o為后綴的目標文件。
3.匯編(Assembly):匯編過程是針對匯編語言的步驟,調用as進行工作,一般來講,.S為后綴的匯編語言源代碼文件、.s為后綴的匯編語言文件經過預編譯和匯編之后都生成以.o為后綴的目標文件。
4.鏈接(Linking):當所有的目標文件都生成之后,gcc就調用ld來完成最后的關鍵性工作,這個階段就是連接。在連接階段,所有的目標文件被安排在可執行程序中的恰當的位置,同時,該程序所調用到 的庫函數也從各自所在的檔案庫中連到合適的地方。
實例:
1.編寫hello.c文件
2.預編譯過程:
gcc -E ./hello.c -o hello.i //.i 為后綴的文件,是已經預處理過的C源代碼文件,可以省略這一步。
cat hellp.c | wc -l //查看hello.c文件內容的行數。
cat hellp.i | wc -l //查看hello.i文件內容的行數。
3.匯編過程:
gcc -S hello.i -o hello.s //.s為后綴的文件,是匯編語言源代碼文件;可以省略這一步。
4.編譯過程
gcc -c ./hello.c //在當前文件夾下生成hello.o .o為后綴的文件,是編譯后的目標文件;
gcc -c hello.c -o hello.o //在當前文件夾下生成hello.o
5.鏈接過程:
gcc hello.o -o hello
6.直接在終端輸入文件路徑或者把hello文件拖動到終端即可執行
用g++編譯c++源程序
用g++編譯c++源程序和c語言類似,可將gcc改為g++逐個嘗試。以下只提供一些簡單介紹:
-E Preprocess only; do not compile, assemble or link
-S Compile only; do not assemble or link
-c Compile and assemble, but do not link
-o Place the output into
-g Use of extra debugging information
-w 關閉編譯時的警告
-o 參數謹慎使用,也許開了編譯優化會出現問題,但是開了編譯優化代碼真的會變快,做好測試就行
二、gdb調試
assert斷言函數 如果參數expression等於零,一個錯誤消息將會寫入到設備的標准錯誤集並且會調用abort函數,就會結束程序的執行。這個雖然可以找到錯誤,但是我們有更厲害的東西
gdb的其實是一個可執行文件,所以我們需要先編譯出這個文件

#include <stdio.h> int main() { int a = 0; printf("%d\n", a++); printf("%d\n", a--); printf("%d\n", ++a); printf("%d\n", --a); }
gcc -g A.c -o A
命令中出現了-g參數,這個不僅可以創建符號表,符號表包含了程序中使用的變量名稱的列表,而且可以關閉所有的優化機制,以便程序執行過程中嚴格按照原來的C代碼進行。
一定要記得加入這個參數
輸入gdb就可以有了,當然你可能不想看到那一坨,可以加-q參數得到一個清爽的界面
gdb后可以file 跟上文件名,也可以進入之后再指定
[root@BobHuang ~]# gdb -q A
Reading symbols from /root/A...done.
(gdb) file A
想回過頭看看以前的代碼,就用list,一次可以顯示十行,繼續list可以顯示接下來的十行
(gdb) list
1 #include<stdio.h>
2 int main()
3 {
4 int a=0;
5 printf("%d\n",a++);
6 printf("%d\n",a--);
7 printf("%d\n",++a);
8 printf("%d\n",--a);
9 }
10
list默認參數可以用show listsize來查看,如果感覺10行太多或者太少,還可以用set listsize <count>來更改。
但是我有時候只是想部分,就可以給list加上參數
list 還可以加上其他參數,比如:
list 5,10 顯示第5行到第10行的代碼;
list func 顯示func函數周圍的代碼,顯示范圍和list參數有關;
list test.c:5,10 顯示源文件test.c第5行到第10行的代碼,一般用於調試含多個源文件的程序。
gdb 還支持字符串查找,search str,從當前行開始,向前查找含str的字符串;
reverse-search str,從當前行開始,向后查找含str的字符串。
在gdb里也可以使用shell+命令,比如
shell clear
就會完成清屏
然后就可以設置斷點了,和在圖形界面類似,可以設置在某一行斷點。甚至可以直接寫一個判斷表達式
(gdb) break 5
Breakpoint 1 at 0x40052c: file A.c, line 5.
(gdb) break 6 if a==1
Breakpoint 2 at 0x400546: file A.c, line 6.
(gdb) break 7 if a==1
Breakpoint 3 at 0x400560: file A.c, line 7.
然后可以通過info breakpoints來查看斷點
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000040052c in main at A.c:5
2 breakpoint keep y 0x0000000000400546 in main at A.c:6
stop only if a==1
3 breakpoint keep y 0x0000000000400560 in main at A.c:7
stop only if a==1
Num表示斷點的編號;Type表示斷點的斷點的類型,第二個斷點類型還加上了條件;Disp表示中斷點在執行一次之后是否失去作用,dis為是,keep為不是;Enb表示當前中斷點是否有效,y為是,n為否;Address表示中斷點所處的內存地址;What指出斷點所處的位置。
(gdb) run
Starting program: /root/A
Breakpoint 1, main () at A.c:5
5 printf("%d\n",a++);
Missing separate debuginfos, use: debuginfo-install glibc-2.17-260.el7_6.5.x86_64
(gdb) continue
Continuing.
0
Breakpoint 2, main () at A.c:6
6 printf("%d\n",a--);
(gdb) continue
Continuing.
1
1
0
[Inferior 1 (process 32035) exited with code 02]
但是他提示我軟件沒裝啊,我們裝一下
(gdb) shell debuginfo-install glibc-2.17-260.el7_6.5.x86_64
再次運行,沒有變化,也就是沒有經過breakpoint3,也就證明了,執行斷點3時a!=1,所以這個判斷非常好用啊,能檢測出某些異常,不過bug復現是不太好實現
接下來就是刪除斷點了。如果不需要程序在該斷點暫停時,有兩種方法,一種是使該斷點失效,一種是直接刪除該斷點。使斷點失效用的是Num,刪除斷點用的是行,這樣就巧妙完成了需求
(gdb) disable 2
(gdb) info breakpoints
Num Type Disp Enb Address What
2 breakpoint keep n 0x000000000040052c in main at A.c:5
(gdb) clear 5
Deleted breakpoint 1
delete命令后面的參數也為Num;可以一次刪除多個斷點,斷點編號之間用空格隔開;如果delete后沒有參數,默認刪除所有斷點,會給出提示選擇是否操作。
上面雖然展示了一下,但是我們需要更多的演示,才展示他的強大
run,開始運行程序;
continue,程序暫停時繼續運行程序的命令;
print 變量名或表達式,打印該變量或者該表達式的值。whatis 變量名或者表達式,可以顯示該變量或表達式的數據類型。
print 變量=值,這種形式還可以給對應的變量賦值;類似的還有set variable 變量=值。作用和用print賦值相同。
next,繼續執行下一條語句;
還有一條命令step,與之類似,不同的是,當下一條語句遇到函數調用的時候,next不會跟蹤進入函數,而是繼續執行下面的語句,而step命令則會跟蹤進入函數內部。
(gdb) run Starting program: /root/A Breakpoint 2, main () at A.c:5 5 printf("%d\n",a++); (gdb) next //繼續執行下一條語句,只執行一條 0 6 printf("%d\n",a--); (gdb) continue //讓程序繼續運行,直到下個斷點或者結束 Continuing. 1 1 0 [Inferior 1 (process 6553) exited with code 02]
直接賦值的結果
(gdb) run Starting program: /root/A Breakpoint 2, main () at A.c:5 5 printf("%d\n",a++); (gdb) print a=10 $1 = 10 (gdb) continue Continuing. 10 11 11 10 [Inferior 1 (process 6693) exited with code 03]
還有nexti和stepi命令,這兩個是單步執行一條機器指令,比如(i=0;i<n;i++)這條語句需要輸入多個nexti才能執行完;兩個的區別和上面相同。
quit,退出gdb調試,如果調試中想要退出,可以直接輸入該命令,會出現提示選擇是否退出。kill命令,結束當前程序的調試,(不會退出gdb)。
三、makefile編寫
makefile帶來直接好處就是——“自動化編譯”。一旦寫好,只需要一個make命令,整個工程完全自動編譯,所以十分方便。而Makefile文件就是告訴make命令怎么樣地去編譯和鏈接程序。但是想要比較靈活的運用它,還是先要熟悉一些關於系統對程序編譯和鏈接的知識。