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
預處理文件的最后結果如下:
我們可以看到文件比原來大了好多,預處理將頭文件iostream展開,其他部分基本不變。
2.編譯-Compiling
編譯生成.s文件,編譯過程是將C代碼轉換成匯編語言。
# -S 編譯選項告訴 g++ 為 C++ 代碼產生匯編語言后停止編譯
# g++ 產生的匯編語言文件的缺省擴展名是 .s
g++ -S test.i -o test.s
產生的.s文件的部分結果如下:
可以看多很多匯編指令。
3.匯編-Assembling
匯編是將編譯生成的.s文件轉換為二進制語言,生成一個.o文件。
# -c 編譯選項告訴 g++ 為 C++ 代碼編譯為機器語言停止編譯
# 缺省時 g++ 建立的目標代碼有一個 .o的擴展名
g++ -c test.s -o test.o
test.o文件部分結果如如下:
可以看到是亂碼,這是因為vim不識別機器語言導致的。
4.鏈接-Linking
鏈接需要的其他文件后生成可執行文件。
# -o 編譯選項來為將產生的可執行文件用指定的文件名
g++ test.o -o test
運行可執行文件,使用./test來運行,結果如下:

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
- -Wall 打印警告信息
- -w 關閉警告信息
- -std=c++11 設置編譯標准
- -o指定輸出文件名 缺省會輸出一個a.out的文件
- -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++編譯器對目錄結構如下的多文件程序進行編譯。
三個文件的內容如下:
// 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的可執行文件,執行結果如下:  --- ####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運行該文件,運行結果如下:

- 鏈接動態庫生成可執行文件:
進入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
運行結果如下:

###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>