Linux系統 利用g++進行C++的多文件調試運行


Linux系統下大家的編譯器環境應該都是安裝的gcc編譯器,調試器是gdb,我們可以通過gcc編譯器對C++文件的編譯過程,對源代碼的編譯過程有一個更好的了解,這有助於我們自己編寫一些vscode的js文件。

1.利用gcc編譯器進行C++多文件編譯和運行


首先我們要了解gcc的編譯過程。

1.1 gcc編譯過程


gcc編譯過程分為4步:預處理、編譯、匯編、鏈接
下面以一個簡單的輸出文件test.cpp來演示gcc的這四步編譯過程。
test.cpp代碼如下:

#include <iostream>
using namespace std;
int main()
{
        cout << "This my first cpp programer in Linux" << endl;
        return 0;
}

1.預處理-Pre-Procession
預處理生成的是.i文件,實際上就是將include的頭文件展開。

# -E 選項指示編譯器對輸入文件進行預處理
g++ -E test.cpp -o test.i

預處理文件的最后結果如下:
image
我們可以看到文件比原來大了好多,預處理將頭文件iostream展開,其他部分基本不變。

2.編譯-Compiling
編譯生成.s文件,編譯過程是將C代碼轉換成匯編語言。

# -S   編譯選項告訴 g++ 為  C++  代碼產生匯編語言后停止編譯
# g++ 產生的匯編語言文件的缺省擴展名是  .s
g++ -S test.i -o test.s

產生的.s文件的部分結果如下:
image
可以看多很多匯編指令。

3.匯編-Assembling
匯編是將編譯生成的.s文件轉換為二進制語言,生成一個.o文件。

# -c   編譯選項告訴 g++ 為  C++  代碼編譯為機器語言停止編譯
# 缺省時 g++ 建立的目標代碼有一個  .o的擴展名
g++ -c test.s -o test.o

test.o文件部分結果如如下:
image
可以看到是亂碼,這是因為vim不識別機器語言導致的。

4.鏈接-Linking
鏈接需要的其他文件后生成可執行文件。

# -o 編譯選項來為將產生的可執行文件用指定的文件名
g++ test.o -o test

運行可執行文件,使用./test來運行,結果如下:
image

1.2 g++重要編譯參數


1.-g 編譯帶調試信息的可執行文件

# -g 選項告訴 GCC 產生能被 GNU 調試器使用的調試信息,以調試程序。

# 產生帶調試信息的可執行文件test
g++ -g test.cpp -o test

2.-O[n] 優化源代碼

## 所謂優化,例如省略掉代碼中從未使用的變量、直接將常量表達式用結果值代替等等,這些操作會所見所包含的代碼量,提高最終生成的可執行文件的運行效率

# -O 選項告訴 g++ 對源代碼進行基本的優化。這些優化在大多數情況下都會是程序執行的更快。 -O 選項告訴g++產生盡可能小和盡可能快的代碼。

# -O 同時減小代碼的長度和執行時間,效果等價與-O1
# -O0 表示不做優化
# -O1 為默認優化
# -O2 完成-O1的優化之后,進行一些額外的調整工作,如指令調整等
# -O3 包括循環展開和其他一些與處理特性相關的優化工作
# 選項將使編譯的速度比 -O 時慢,但通常產生的代碼執行速度更快。

# 使用 -O2優化源代碼,並輸出可執行文件
g++ -O2 test.cpp -o test_with_O2

3.-l和 -L 指定庫文件 | 指定庫文件路徑

# -l參數(小寫)就是用來指定程序要鏈接的庫,-l參數緊接着就是庫名
# 在/lib和/usr/lib/和usr/local/lib里的庫直接用-l參數就能鏈接

# 鏈接glog庫
g++ -lglog test.cpp

# 如果庫文件沒有放在上面三個目錄里,需要使用-L參數(大寫)制定庫文件所在的目錄
# -L參數跟着的是庫文件所在的目錄名

# 鏈接mytest庫,libmytest.so在/home/bing/mytestlibfolder目錄下
g++ -L/home/bing/mytestlibfolder -l libmytest test.cpp

4.-I 指定頭文件搜索目錄

# -I
# /usr/include目錄一般不用指定,gcc知道去那里找,但是如果頭文件不在/usr/include里我們就要用-I參數指定了,比如頭文件放在/myinclude目錄里,那編譯命令行就要加上-I/myinclude參數了,不加會得到一個“xxxx.h:No such file or directory”的錯誤,-I也可以用相對路徑來指定。

g++ -I/myinclude test.cpp
  1. -Wall 打印警告信息
  2. -w 關閉警告信息
  3. -std=c++11 設置編譯標准
  4. -o指定輸出文件名 缺省會輸出一個a.out的文件
  5. -D 定義宏
# 在使用gcc/g++編譯的時候定義宏

# 常用場景
# -DDEBUG 定義DEBUG宏,可能文件中有DEBUG宏的相關信息,用個DDEBUG來選擇開啟或關閉DEBUG

樣例如下:

// -Dname 定義宏name,默認定義內容為字符串“1”

# include <stdio.h>
{
	using std::cout;
	#ifdef DEBUG
		printf("DEBUG LOG\n");
	#endif
		printf("in\n");
}

1.3 使用g++命令行編譯

使用g++編譯器對目錄結構如下的多文件程序進行編譯。
image
三個文件的內容如下:

// include file and definition of prototype
#include <iostream>
void Swap(int &a, int &b);
// definition of function
#include "Swap.h"
void Swap(int &a, int &b)
{
        int temp;
        temp = a;
        a = b;
        b = temp;
}
// main function
#include "Swap.h"

int main()
{
        using namespace std;
        int a = 10;
        int b = 20;
        cout << "a = " << a << endl;
        cout << "b = " << b << endl;
        Swap(a,b);
        cout << "After Swap(a,b)" << endl;
        cout << "a = " << a << endl;
        cout << "b = " << b << endl;
        return 0;
}
--- ####1.3.1 直接編譯 --- 在終端輸入如下指令, ``` g++ main.cpp ./src/Swap.cpp -Iinclude ``` 會產生一個 a.out的可執行文件,執行結果如下: ![image](https://img2022.cnblogs.com/blog/2636982/202201/2636982-20220123160556048-1279486076.png) --- ####1.3.2 生成庫文件並編譯 --- - 鏈接**靜態庫**生成可執行文件: ``` ## 進入src目錄下 cd src

匯編,生成Swap.o文件 -c和-I的順序可以顛倒

g++ Swap.cpp -c -I../include

生成靜態庫libSwap.a

ar rs libSwap.a Swap.o

回到上級目錄

cd ..

鏈接,生成可執行文件:staticmain

g++ main.cpp -Iinclude -lSwap -Lsrc -o staticmain

通過./staticmain運行該文件,運行結果如下:
![image](https://img2022.cnblogs.com/blog/2636982/202201/2636982-20220123161728014-1511558784.png)

- 鏈接動態庫生成可執行文件:

進入src目錄下

cd src

生成動態庫libSwap.so

g++ Swap.cpp -I../include -fPIC -shared -o libSwap.so

上面的命令等價於以下兩條命令

g++ Swap.cpp -I../include -c -fPIC

gcc -shared -o libSwap.so Swap.o

回到上級目錄

cd ..

鏈接,生成可執行文件:dyna_main

g++ main.cpp -Iinclude -lSwap -Lsrc -o dyna_main

輸入./dyna_main,來運行該文件,會提示運行錯誤:
./dyna_main: error while loading shared libraries: libSwap.so: cannot open shared object file: No such file or directory
出現該錯誤的原因是需要添加libSwap.so的路徑,通過以下命令運行:
LD_LIBRARY_PATH=src ./dyna_main
運行結果如下:
![image](https://img2022.cnblogs.com/blog/2636982/202201/2636982-20220123164957382-1595077492.png)

###2. 使用gdb調試器對代碼進行調試
GDB介紹:
- GDB(GNU Debugger)是一個用來調試C/C++程序的功能強大的調試器,是Linux系統開發C/C++最常用的調試器。
- 程序員可以使用**GDB來跟蹤程序中的錯誤**,從而減少程序員的工作量。
- Vscode是通過GDB調試其來實現C/C++調試工作的。

GDB主要功能:
- 設置斷點(斷點可以是條件表達式)
- 使程序在指定代碼上暫停執行,便於觀察
- 單步執行程序,便於調試
- 查看程序中變量值的變化
- 動態改變程序的執行環境
- 分析崩潰程序產生的core文件

####4.1 常用調試命令參數
調試執行: 執行gdb[exefilename],進入gdb調試程序,其中exefilename為要調試的可執行文件名

以下命令后括號內為命令的簡化使用,比如rur(r),輸入r就代表run

(gdb)help(h) # 查看命令幫助, 在gdb中輸入help + 命令

(gdb)run(r) # 重新開始運行文件(run-text:加載文本文件,run-bin:加載二進制文件)

(gdb)start # 單步執行,運行程序,停在第一行執行語句

(gdb)list(l) # 查看源代碼(list-n,從第n行開始查看代碼。list + 函數名:查看具體函數)

(gdb)set # 設置變量的值

(gdb)next(n) # 單步調試(逐過程,函數直接執行)

(gdb)step(s) # 單步調試(逐語句:跳入自定義函數內部執行)

(gdb)backtrace(bt) # 查看函數的調用的棧幀和層級關系

(gdb)frame(f) # 切換函數的棧幀

(gdb)info(i) #查看函數內部變量的數值

(gdb)finish # 結束當前函數,返回到函數調用點

(gdb)continue(c) # 繼續運行

(gdb)print(p) #打印值及地址

(gdb)quit(q) # 退出gdb


(gdb)break+num(b) # 在num行設置斷點

(gdb)info breakpoints # 查看當前設置的所有斷點

(gdb)delete breakpoints num(d) #刪除第num個斷點

(gdb)display # 追蹤查看具體變量值

(gdb)undisplay # 取消追蹤觀察變量

(gdb)watch # 被設置觀察點的變量發生修改時,打印顯示

(gdb)i watch # 顯示觀察點

(gdb)enable breakpoints # 啟用斷點

(gdb)disable breakpoints # 禁用斷點

(gdb)x # 查看內存x/20xw 顯示20個單元,16進制,4字節沒單元

(gdb)run argv[1] argv[2] # 調試時命令行傳參

(gdb)set follow-fork-mode child # Makefile項目管理:選擇跟蹤父子進程(fork())

**Tips:<br>1.編譯程序時加上-g,之后才能用gdb -g main.c -o main<br>2.回車鍵:重復上一命令**

</font>


免責聲明!

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



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