GCC的全稱是GNU Compiler Collection,是GNU工具鏈中的一種。GCC不僅支持C/C++語言,還支持Fortran/Ada/Java等語言的編譯。GCC和gcc是兩個概念,GCC是工具鏈的集合,裡面除了gcc/g++還包含了ccl,cclplus等組件。gcc/g++只是GCC工具鏈的一個子集。 ## g++和gcc的區別 gcc可以判斷出目標程序所使用編程語言的類別,會把xxx.c文件當作C語言編譯,把xxx.cpp文件當作C++語言編譯。而g++只把xxx.c和xxx.cpp一律都當作C++語言來編譯。在編譯C++文件的時候,g++會自動鏈接一些標準庫或基礎庫,而gcc不會。當正在編譯的C++代碼文件依賴STL標準庫的時候,為了使用STL,gcc命令需要增加參數`–lstdc++`。因此,雖然gcc和g++都可以編譯C++語言程序,但是使用g++會更方便一些。 ## 常見文件副檔名 - 目標文件: - `xxx.o`:Linux, Mac - `xxx.obj`:windows - 二進製文件: - `xxx`(沒有副檔名):Linux, Mac, FreeBSD, - `xxx.exe`:windows - `xxx.hex`:嵌入式系統 - 共享庫文件,也叫動態庫文件: - `xxx.dll`:windows - `xxx.so`:Linux - `xxx.dylib`:Mac - 靜態庫文件 - `xxx.a` ## C/C++語言的編譯過程 1. 預處理 預處理命令聲明了編譯時需要的各種頭文件和宏,比如包含哪些頭文件、宏定義的擴展、在哪個代碼段做條件編譯等。涉及預處理的語法有:#define,#include,#ifdef...#endif 2. 編譯 首先檢查代碼的規範性和語法錯誤等,檢查完畢後把代碼翻譯成彙編語言,生成彙編語言文件 3. 彙編 基於彙編語言文件生成二進制格式的目標文件 4. 鏈接 將目標代碼與所依賴的庫文件進行關聯或者組裝,合成一個可執行文件 具體過程如圖: ![[Pasted image 20220926211701.png]] ### 拿g++舉例 1. 樣例代碼: ```cpp #include int main() {     std::cout << "Hello World!" << std::endl;     return 0; } ``` 2. g++的編譯第一步是預處理:將`xx.cpp`源文件預處理成`xx.i`文件 ```cpp g++ -E demo.cpp -o demo.i ``` 3. 第二步是編譯:將`xx.i`文件編譯為`xx.s`的組合語言文件。此時只進行編譯生成組合語言程式碼,而不對代碼以彙編的方式調試 ```js g++ -S demo.i -o demo.s ``` 3. 第三步是彙編:將`xx.s`文件彙編成`xx.o`的二進制目標文件 ```cpp g++ -c demo.s -o demo.o ``` 4. 第四步是鏈接:將`xx.o`二進製文件進行鏈接,最終生成可執行程序 ```cpp g++ demo.o -o demo.out ``` ![[Pasted image 20220926212043.png]] ## 靜態鏈接和動態鏈接的區別 ### 靜態庫 與目標程序合併,成為目標程序的一部分。 創建靜態庫的時候,需要使用`gcc/g++ -c`先將xxx.c源文件編譯為目標文件xxx.o,然後使用`ar`指令將xxx.o打包成xxxx.a靜態庫。 目標程序與靜態庫鏈接時,目標程序代碼調用的任何外部函數的代碼都會從靜態庫中復製到最終的可執行文件中。 GCC在鏈接時優先使用動態庫,只有當動態庫不存在時才開始使用靜態庫,如果要強制使用靜態庫,編譯時加上`-static`參數。 使用`-Wl`、`-Bstatic`告訴鏈接器優先使用靜態庫。 ### 動態庫 不包含在目標程序中,但是與目標程序相關聯。 創建動態庫的時候,可以傳`-shared`和`-fPIC`參數,`-fPIC`參數用於編譯階段,用來生成位置無關的代碼。使用`gcc -shared -fPIC`可以直接用xxx.c源文件生成xxx.so動態庫。 目標程序與動態庫鏈接時,可執行文件僅包含它所需的一個小函數表,而不是來自庫文件的完整機器代碼。在可執行文件開始運行之前,動態庫的代碼被操作系統複製到內存中進行共享。 動態庫之所以叫共享庫,可能是由於動態庫的代碼副本可以在多個程序之間共享。正因為這種鏈接方式,共享庫每次被更新時,都不需要重新編譯正在使用共享庫的目標程序。 使用`-Wl`、`-Bdynamic`告訴鏈接器優先使用動態庫。 ### 有關的環境變量 `LIBRARY_PATH`:使用於編譯期間,目標程序鏈接時搜索動態庫的路徑。 `LD_LIBRARY_PATH`:使用於目標程序生成後,目標程序運行時搜索動態庫的路徑 ### 靜態庫鏈接時,搜索庫文件路徑的順序 1. `ld`會去找GCC命令中的參數`-L` 2. gcc的環境變量`LIBRARY_PATH` 3. `/lib`,`/usr/lib`,`/usr/local/lib`等寫在程序內的路徑 ### 動態庫鏈接時,搜索庫文件路徑的順序 1. 編譯目標代碼時指定的動態庫搜索路徑 2. gcc的環境變量`LD_LIBRARY_PATH` 3. 配置文件`/etc/ld.so.conf`中指定的動態庫搜索路徑 4. 默認的動態庫搜索路徑`/lib` 5. 默認的動態庫搜索路徑`/usr/lib` ## 實用的工具 ### ldd 列出依賴的動態庫 ![[Pasted image 20220926212505.png]] ### nm 查看動態庫/靜態庫中的函數 ![[Pasted image 20220926212522.png]] ## gcc/g++命令常見參數 命令格式 ```bash gcc [-c|-S|-E] [-std=standard]            [-g] [-pg] [-Olevel]            [-Wwarn...] [-pedantic]            [-Idir...] [-Ldir...]            [-Dmacro[=defn]...] [-Umacro]            [-foption...] [-mmachine-option...]            [-o outfile] [@file] infile... ``` 在linux環境使用:`man g++`可以查看g++命令常用參數 ### 常見參數如下(注意大小寫): ```bash -o #输出到指定文件。如果不指定,默认输出到a.out -E #仅进行预处理,不进行编译、汇编和链接 -S #将代码转换为文件格式为xxx.s的汇编语言文件,但不进行汇编 -c #仅进行编译和汇编,不进行链接操作,常用于编译不包含main程序的子程序代码 -v #打印gcc编译时的详细步骤信息 复制代码 ``` ### 編譯和路徑參數 ```bash -l[basic library] #编译时指定要使用的基础库,样例:-lpthread,针对Posix线程共享库进行编译 -L[shared-library path] #共享库的路径添加到搜索的范围,路径为包含xxx.dll/xxx.so/xxx.dlyb文件的目录 -I[include header-file path] #将头文件的路径添加到搜索的范围,路径为包含xxx.h/xxx.hpp文件的目录 -shared #生成共享库,库文件格式为xxx.dll/xxx.so/xxx.dlyb格式的文件 -static #生成静态库,库文件格式为xxx.a格式的文件 -Wl #告诉编译器将后面的参数传递给链接器 -Wl,-Bstatic #-Bstatic选项用于对指定的库静态连接 -Wl,-Bdynamic #-Bdynamic搜索共享库(默认) -Wa,option #此选项传递option给汇编程序;如果option中间有逗号,就将option分成多个选项,然后传递给会汇编程序 -Wl,option #此选项传递option给连接程序;如果option中间有逗号,就将option分成多个选项,然后传递给会连接程序 复制代码 ``` ### 預處理參數 ```bash #使用形式:-D[FLAG] 或-D[FLAG]=VALUE -Dmacro #在命令行里定义宏,相当于C语言中的"#define macro" -Umacro #相当于C语言中的"#undef macro" -undef #取消对任何非标准宏的定义 复制代码 ``` ### 警告與報錯參數 ```bash -Wall #发出gcc提供的所有有用的报警信息 -Werror #将警告升级为编译报错 -Wextra / -W #启用-Wall未启用的额外警告位,对合法但值得怀疑的代码发出警告 例如 -Wsign-compare -pendantic / -Wpendantic #发出ISO C和ISO C++标准列出的所有警告,用于语法检查,-pedantic-erros的用法也类似 -fsyntax-only #仅做语法检查 复制代码 ``` ### 調試參數 ```bash -g #产生带有调试信息的目标代码 -gstabs #此选项以stabs格式声称调试信息,但是不包括gdb调试信息 -gstabs+ #此选项以stabs格式声称调试信息,并且包含仅供gdb使用的额外调试信息 -ggdb #生成gdb专用的调试信息 -glevel #请求生成调试信息,同时用level指出需要多少信息,默认的level值是2 复制代码 ``` ### 編碼配置參數 ```bash -fno-exceptions #屏蔽掉C++的异常,常用于于嵌入式或无法接受异常的系统 -fno-rtti #禁用RTTI,常用于嵌入式或游戏开发 -fno-asm #不要识别asm,inline或typeof作为关键字,以便代码可以使用这些词作为标识符。您可以使用关键字__asm__,__inline__来__typeof__ 代替。 -ansi暗示-fno-asm -fPIC / -fpic #让编译器的代码和位置无关,让代码逻辑不使用绝对地址,只用相对地址,方便文件加载 -nostdinc #使编译器不再系统默认的头文件目录里面找头文件, 一般和 -I 联合使用,明确限定头文件的位置 -nostdin C++ #规定不在g++指定的标准路经中搜索,但仍在其他路径中搜索,.此选项在创建libg++库使用 复制代码 ``` ### 優化參數 ```bash -O0 #不优化 -O1 / -O #尝试优化编译时间和可执行文件大小 -O2 #尝试所有的优化选项,但不会进行“空间换时间”的优化方式 -Os #尝试所有的优化选项时,优先优化可执行文件大小 复制代码 ``` ## 來源 - [C/C++生態工具鏈——gcc/g++編譯器使用指南- 掘金](https://juejin.cn/post/7143280156042330125) ## 參考 - [Top (Using the GNU Compiler Collection (GCC))](https://gcc.gnu.org/onlinedocs/gcc/) - [The C++ Compilation Model | C++ Fundamentals](https://subscription.packtpub.com/book/programming/9781789801491/1/ch01lvl1sec03/the-c-compilation-model) - [What is LD_LIBRARY_PATH used for?](https://linuxhint.com/what-is-ld-library-path/)