Linux C/C++開發


首先就是要熟練在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);
}
A.c
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命令怎么樣地去編譯和鏈接程序。但是想要比較靈活的運用它,還是先要熟悉一些關於系統對程序編譯和鏈接的知識。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM