Files
Obsidian-Main/01. 個人/02. 專注Study/C++/GCC.md
Awin Huang 65ea73f46b vault backup: 2022-09-26 21:24:06
Affected files:
01. 個人/02. 專注Study/C++/GCC.md
attachments/Pasted image 20220926211701.png
attachments/Pasted image 20220926212043.png
2022-09-26 21:24:06 +08:00

89 lines
4.4 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
GCC的全稱是GNU Compiler Collection是GNU工具鏈中的一種。GCC不僅支持C/C++語言還支持Fortran/Ada/Java等語言的編譯。GCC和gcc是兩個概念GCC是工具鏈的集合裡面除了gcc/g++還包含了cclcclplus等組件。gcc/g++只是GCC工具鏈的一個子集。
## g++和gcc的區別
gcc可以判斷出目標程序所使用編程語言的類別會把xxx.c文件當作C語言編譯把xxx.cpp文件當作C++語言編譯。而g++只把xxx.c和xxx.cpp一律都當作C++語言來編譯。在編譯C++文件的時候g++會自動鏈接一些標準庫或基礎庫而gcc不會。當正在編譯的C++代碼文件依賴STL標準庫的時候為了使用STLgcc命令需要增加參數`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 <iostream>
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`等寫在程序內的路徑