GCC(GNU Compiler Collection)是自由軟體基石,從最初的 C 編譯器發展為支援 C、C++、Java、Ada、Fortran、Go 等多語言的編譯器套件。對嵌入式 Linux 開發者而言,精通 GCC 的四階段編譯流程——預處理、編譯、組譯、連結,是排錯、優效能、交叉編譯的關鍵能力。本文以實戰範例逐層拆解,並附上常用副檔名對照表。
| 副檔名 | 對應語言/類型 | 經過階段 |
|---|---|---|
| .c | C 原始碼 | 預處理 → 編譯 → 組譯 |
| .cc / .cpp / .cxx | C++ 原始碼 | 預處理 → 編譯 → 組譯 |
| .m | Objective-C | 預處理 → 編譯 → 組譯 |
| .i | 已預處理的 C 碼 | 編譯 → 組譯 |
| .ii | 已預處理的 C++ 碼 | 編譯 → 組譯 |
| .s / .S | 組合語言原始碼 | 組譯 |
| .h | 標頭檔 | (不直接編譯,由 #include 引入) |
| .o | 目標檔 | 連結 |
| .a / .so | 靜態/動態函式庫 | 連結 |
準備兩個檔案:
/* hello.h */
#ifndef _HELLO_H_
#define _HELLO_H_
typedef unsigned long val32_t;
#endif
/* hello.c */
#include
#include
#include "hello.h"
int main(int argc, char *argv[]) {
val32_t i = 5;
printf("hello, embedded world %d\n", i);
return 0;
}
-E展開巨集、處理 #include、#define、條件編譯,產出 .i 檔:
gcc -E -o hello.i hello.c
hello.i 內容可見 hello.h 的 typedef 已內嵌,註解被移除,並插入行號標記供後除錯用。
-S語法檢查、語義分析、中間碼生成、優化,輸出組合語言 .s:
gcc -S -o hello.s hello.i
產出的 hello.s 包含 x86 組合指令(pushl %ebp, movl %esp, %ebp 等),展示 C 程式如何對應堆疊幀建立、參數傳遞、呼叫 printf。
-c將組合語言翻譯為機器碼目標檔 .o(二進位,含符號表、重定位資訊):
gcc -c -o hello.o hello.s
解析未定義符號(如 printf),連結標準庫 libc.so.6 及啟動碼 crt1.o 等,產出最終執行檔:
gcc -o hello hello.o
執行 ./hello 輸出:hello, embedded world 5。
stdio.h 僅提供 printf 宣告,實作在 libc.so.6。/usr/lib、/lib 並自動連結 -lc(libc)。-static 強制靜態連結(體積大、無相依);預設動態連結(需執行時載入器 ld-linux.so 協助)。arm-linux-gnueabihf-gcc 等前綴工具鏈,流程完全一致,只針對不同目標架構。-O2:平衡速度與大小的優化等級(發布建議)。-g:產生 DWARF 除錯資訊,配合 GDB 使用。-Wall -Wextra:開啟多數警告,捕捉潛在缺陷。-std=c11:指定 C 標準版本。-v:顯示完整編譯驅動程式呼叫細節,適合診斷路徑/版本問題。掌握 GCC 四階段流程,不只是知道指令怎麼下,更能在編譯錯誤、連結失敗、執行時崩潰時,快速定位是「巨集展開錯」、「語法錯」、「符號未定義」還是「ABI 不相容」。這是從「會用工具」進階為「駕馭工具」的分水嶺。