首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

VC++ 程式編譯鏈接原理與過程完整圖解

     編輯:VC 教學

前言

理解原始碼如何變成可執行檔,是除錯連結錯誤、優化建置速度、處理跨模組符號衝突的基礎。本文以 VC++ 專案 EX10 (含 EX10.cpp, fish.cpp, animal.cpp) 為例,詳細說明**預處理 → 編譯 → 目標檔 → 鏈接 → EXE** 完整流程與中間產物。

建置輸出實例


Compiling...
EX10.cpp
fish.cpp
animal.cpp
Linking...
EX10.exe

兩大階段:編譯與鏈接

階段 1:編譯 — 產生目標檔

  1. 預處理:處理 #include, #define, #if 等指令,展開巨集、插入標頭檔內容、移除註解,輸出**翻譯單元** (邏輯上單一巨大原始檔)。
  2. 編譯器:接收翻譯單元,進行詞法/語法/語義分析、優化、程式碼生成,輸出 **COFF 目標檔** (.obj)。
    • 每個 .cpp 獨立編譯 → 對應一個 .obj。
    • 標頭檔不參與編譯,僅透過 #include 被插入翻譯單元。
    • 目標檔包含:機器碼、符號表 (函數/變數名、型別)、重定位資訊、除錯資訊。

階段 2:鏈接 — 產生 EXE

  1. 鏈接器 讀取所有 .obj、指定的 .lib (靜態庫/導入庫)、系統庫。
  2. 符號解析:將各 .obj 中的外部參考 (如 animal::eat()) 解析到定義處。
  3. 地址指派:決定各區段 (code, data, bss...) 在虛擬記憶體中的相對位址 (RVA)。
  4. 產生 PE 格式 EXE:包含 DOS Header、PE Header、區段表、區段資料、匯入/匯出表、資源、除錯目錄等。

完整流程圖解


+----------+     +------------+     +--------+     +----------+     +-----+
| EX10.cpp | --> | 預處理器   | --> | 編譯器 | --> | EX10.obj |     |
+----------+     +------------+     +--------+     +----------+     |
                                                                  |
+----------+     +------------+     +--------+     +----------+   |  +-----------------+
| fish.cpp | --> | 預處理器   | --> | 編譯器 | --> | fish.obj | --+->| 鏈接器          |
+----------+     +------------+     +--------+     +----------+   |  | - 符號解析      |
                                                                  |  | - 地址指派      |
+----------+     +------------+     +--------+     +----------+   |  | - 產生 EXE      |
|animal.cpp| --> | 預處理器   | --> | 編譯器 | --> |animal.obj| --+->| - 輸出 EX10.exe |
+----------+     +------------+     +--------+     +----------+      +-----------------+
       ^                                                              |
       |                     靜態庫 (.lib) / 系統庫                    |
       +--------------------------------------------------------------+

關鍵觀念釐清

觀念說明
編譯單位 = .cpp + 所含 #include每個 .cpp 獨立編譯,互不干擾。標頭檔重複包含在多個 .cpp 會導致程式碼膨脹 (模板/inline 函數)。
.obj 不包含最終位址目標檔使用相對位移,最終虛擬位址由鏈接器指派 (ASLR 可能再隨機化)。
外部符號 = 宣告無定義extern int g; 在 .obj 中標記為外部符號,鏈接時必須在其他 .obj/.lib 找到定義 int g = 0;。
內聯函數可能不產生符號inline 函數若被內展,目標檔不含其符號;若取址或無法內展則會產生弱符號 (COMDAT)。
鏈接順序影響靜態庫解析傳統鏈接器單向掃描 .lib,若 A.lib 依賴 B.lib,命令列須 A.lib B.lib 或重複列出。

Debug 目錄產物

專案 Debug/ 目錄下可見:

  • EX10.obj, fish.obj, animal.obj — 目標檔
  • EX10.pdb — 程式資料庫 (除錯符號)
  • EX10.ilk — 增量鏈接狀態
  • EX10.exe — 最終可執行檔
  • vc60.pdb / vc70.pdb — 編譯器產生的型別資訊 (舊版)

常見建置錯誤對應階段

錯誤類型階段典型原因
語法錯誤 / 型別不符編譯原始碼錯誤、標頭檔不一致
未定義識別符編譯缺少 #include、拼寫錯誤
LNK2001 未解析的外部符號鏈接缺少 .lib、函數僅宣告未定義、專案未加入 .cpp
LNK2005 符號重複定義鏈接多個 .cpp 定義同一全域函數/變數、標頭檔定義非 inline 變數
LNK1104 無法開啟檔案鏈接.lib 路徑錯誤、檔案被佔用、磁碟空間不足

總結

編譯將**翻譯單元**轉為**目標檔**,鏈接將**目標檔 + 庫**合併為 **PE EXE**。掌握此流程,可精準定位建置錯誤、理解符號可見性、優化建置依賴關係 (如前置宣告減少 #include、統一 PCH)、正確管理靜態/動態庫鏈接順序。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved