不同於直譯語言,C 程式必須經過「預處理 → 編譯 → 組裝 → 連結」四大階段才能生成可執行檔。理解此流程對除錯、優化、跨平台建置至關重要。本文系統拆解各階段產物、工具鏈與常用 GCC 參數。
| 副檔名 | 名稱 | 內容 | 階段產出 |
|---|---|---|---|
| .c | 原始碼 | C 程式碼 | 開發者編寫 |
| .h | 標頭檔 | 宣告、巨集、內聯函式 | 開發者編寫 |
| .i | 預處理後原始碼 | 展開巨集、包含標頭檔、移除註解 | 預處理器 (cpp) |
| .s | 組合語言 | CPU 指令助憶符 | 編譯器 (cc1) |
| .o / .obj | 目標檔 | 機器碼、符號表、重定位資訊 | 組裝器 (as) |
| .out / .exe | 可執行檔 | 完整機器碼、載入資訊、動態連結資訊 | 連結器 (ld) |
| .a / .lib | 靜態庫 | 目標檔封存 | ar |
| .so / .dll | 動態庫 | 共享機器碼、導出表 | 連結器 |
由 cpp (C PreProcessor) 自動調用,主要工作:
#include:將標頭檔內容原文插入#define 巨集:文字替換#if/#ifdef/#ifndef/#else/#endif 根據條件保留/捨棄程式碼// 與 /* */查看產物:gcc -E hello.c -o hello.i 或 gcc -save-temps hello.c 保留中間檔
由編譯器核心(如 cc1)將 .i 轉為 .s 組合語言:
-O 等級)常用指令:
# 僅編譯至目標檔(不連結)
gcc -c hello.c -o hello.o
# 保留組合語言檔
gcc -S hello.c -o hello.s
# 開啟嚴格警告(強烈建議)
gcc -Wall -Wextra -pedantic -std=c11 -c hello.c
由 as (GNU Assembler) 將 .s 轉為 .o 二進位目標檔:
通常由編譯器驅動程式自動呼叫,無需手動操作。
由 ld (GNU Linker) 將多個 .o 與庫檔合併為可執行檔:
printf)綁定至庫檔定義靜態連結 vs 動態連結:
gcc -static hello.o -o hello,庫碼嵌入可執行檔,體積大但獨立gcc hello.o -o hello,執行時由動態連結器載入 libc.so 等# 完整建置(預處理+編譯+組裝+連結)
gcc hello.c -o hello
# 指定輸出檔名
gcc hello.c -o myapp
# 多檔案分別編譯再連結(大型專案標準流程)
gcc -c main.c -o main.o
gcc -c utils.c -o utils.o
gcc main.o utils.o -o myapp
# 必備警告旗標
gcc -Wall -Wextra -Wshadow -Wconversion -pedantic -std=c11
# 除錯資訊
gcc -g -O0 hello.c -o hello_dbg
# 最佳化等級
gcc -O2 hello.c -o hello_opt # 發布建議
gcc -Os hello.c -o hello_small # 體積優化
gcc、clang、cc 本質是驅動程式,負責:
cpp → cc1 → as → ldLinux 上 cc 通常為 gcc 軟連結;嵌入式交叉編譯則用 arm-linux-gnueabihf-gcc 等前綴工具鏈。
掌握編譯流程,你就能讀懂錯誤訊息中的階段標識(preprocessing/assembler/linker),精準定位問題;也能編寫高效的 Makefile/CMakeLists.txt,實現增量建置與跨平台部署。