四階段流水線
源文件 (.c/.cpp)
│
▼
┌─────────────┐
│ 預處理 │ gcc -E → .i (展開宏、頭文件、條件編譯)
│ (Preprocess)│
└─────────────┘
│
▼
┌─────────────┐
│ 編譯 │ gcc -S → .s (匯編語言,文本可讀)
│ (Compile) │
└─────────────┘
│
▼
┌─────────────┐
│ 匯編 │ gcc -c → .o/.obj (機器碼,目標文件,含符號表、重定位表)
│ (Assemble) │
└─────────────┘
│
▼
┌─────────────┐
│ 鏈接 │ gcc -o → executable (符號解析、地址綁定、庫合併、生成最終二進制)
│ (Link) │
└─────────────┘
關鍵工具鏈
- GCC (GNU Compiler Collection):
gcc (C)、g++ (C++)、gfortran、gcj (Java 前端,已棄用)。
gcc 根據副檔名自動選前端:.c→C、.cpp/.cc/.cxx→C++、.s/.S→匯編。
g++ 等同 gcc -xc++ -lstdc++ -shared-libgcc,自動鏈接 C++ 標準庫。
常用命令組合
| 目的 | 命令 | 說明 |
| 一步到位 | g++ main.cpp utils.cpp -o app | 編譯+鏈接,輸出 app (Linux) / a.exe (Windows) |
| 分步編譯 | g++ -c main.cpp -o main.o
g++ -c utils.cpp -o utils.o
g++ main.o utils.o -o app | 增量構建、分發目標文件、靜態庫制作基礎 |
| 僅預處理 | gcc -E main.c -o main.i | 排查宏展開、頭文件包含順序 |
| 僅生成匯編 | gcc -S main.c -o main.s | 檢查編譯器優化、指令選擇、內聯展開 |
| 指定標準 | g++ -std=c++17 -Wall -Wextra -O2 | C++17、全警告、O2 優化 |
| 靜態鏈接 | g++ -static main.cpp -o app | 無動態庫依賴,體積大、啟動快、許可證風險低 |
| 動態庫制作 | g++ -fPIC -shared utils.cpp -o libutils.so | 位置無關碼、共享庫 |
| 鏈接動態庫 | g++ main.cpp -L. -lutils -Wl,-rpath,. -o app | 運行時搜尋 ./libutils.so |
目標文件結構 (ELF / PE-COFF)
- .text:代碼段 (只讀、可執行)。
- .data:已初始化全局/靜態變量 (讀寫)。
- .bss:未初始化全局/靜態變量 (佔用文件大小為 0,運行時置零)。
- .rodata:只讀數據 (字串常量、const 變量)。
- 符號表:導出符號 (函數、全局變量)、導入符號 (外部引用)、本地符號。
- 重定位表:指導鏈接器修補地址。
鏈接器核心邏輯
- 符號解析:將每個引用綁定到唯一定義 (強符號優於弱符號、多重定義報錯)。
- 段合併:同名段合併 (所有 .text 合併、所有 .data 合併)。
- 地址分配:確定每段在虛擬地址空間的載入位址 (ASLR/PIE 影響)。
- 重定位修補:根據重定位表,將機器碼中的佔位地址替換為真實地址 (PC 相對 / 絕對)。
- 生成可執行文件頭:ELF Header / PE Header,包含入口點、程序頭表、段頭表、動態鏈接信息 (INTERP, NEEDED, RPATH)。
常見鏈接錯誤與對策
| 錯誤 | 原因 | 解決 |
| undefined reference to `foo' | 缺少定義 / 庫未鏈接 / 庫順序錯誤 | 補全實現、-lfoo 放在依賴它的目標文件**後面**、檢查 extern "C" |
| multiple definition of `bar' | 頭文件定義非 inline 變量/函數、多文件包含同一定義 | 頭文件只聲明、定義放 .cpp、或 inline/static |
| relocation truncated to fit | 代碼模型不匹配 (large model / -mcmodel=large) | -mcmodel=medium/large、或拆分大數組 |
| undefined reference to `vtable for Class' | 虛函數聲明無定義、純虛函數未實現、缺少關鍵虛函數定義 | 提供虛析構函數定義、實現所有非純虛函數 |
調試信息與優化權衡
-g:生成 DWARF 調試信息,**不影響優化**,建議發布版也保留 (可 strip 獨立發布)。
-O0:無優化,調試體驗最佳,變量不被優化掉。
-Og:優化等級針對調試優化 (GCC 4.8+),推薦開發預設。
-O2/-O3:發布優化,可能內聯、重排、消除變量,調試困難。
strip:移除符號表/.debug 段,減小體積 30~50%。
現代構建系統
手寫 Makefile → CMake (跨平台、IDE 原生支援) → Meson (快速、聲明式、Python 腳本) → Bazel/Buck (大規模單倉、遠程緩存、增量構建)。核心邏輯不變:**依賴圖 → 拓撲序 → 調用編譯器/鏈接器**。