---
# System prepended metadata

title: 【C++ 筆記】編譯流程（Compilation Process）
tags: [程式設計, C++, 程式語言]

---

【C++ 筆記】編譯流程（Compilation Process）
===

目錄（Table of Contents）：

[TOC]

---

很感謝你點進來這篇文章。

你好，我並不是什麼 C++、程式語言的專家，所以本文若有些錯誤麻煩請各位鞭大力一點，我極需各位的指正及指導！！本系列文章的性質主要以詼諧的口吻，一派輕鬆的態度自學程式語言，如果你喜歡，麻煩留言說聲文章讚讚吧！

編譯流程（Compilation Process）
---

共分四階段：

1. Preprocessing（前置處理）
2. Compilation（編譯）
3. Assembly（組譯）
4. Linking（連結）

![image](https://hackmd.io/_uploads/SyNeCvE5xx.png)

Image Source：[C++ Preprocessor And Preprocessor Directives - GeeksforGeeks](https://www.geeksforgeeks.org/cpp/cpp-preprocessors-and-directives/)

所謂的 Source File 也就是 Source Code，就是 .cpp / .c 檔案。

### 1. Preprocessing

範例指令：`g++ -E main.cpp -o main.i`

Preprocessing 前置處理是 C / C++ 編譯的第一步，主要對 `#` 開頭的指令（稱為 marco 巨集）進行處理。

簡單來說就是把 `#include` 展開（把標頭檔貼進程式），處理 `#define` 巨集、條件編譯這些東西，最後會輸出 `.i` 檔，裡面含一個乾淨的 C / C++ Code 跟插入的標頭檔和展開的巨集們。

接下來會做以下步驟的處理：

1. Macro Definition & Replacement
    - 處理所有 `#define` 指令，把程式中出現的巨集全用定義內容替換掉。
    - 如 `#define PI 3.14` 則將所有 `PI` 換成 `3.14`。
2. File Inclusion
    - 處理 `#include` 指令，把指定的 header file 內容複製貼進 Source Code 在的位置。
    - 如 `#include <stdio.h>` 會把 `stdio.h` 內容插入開頭中，如果他放在開頭的話。
3. Conditional Compilation
    - 處理 `#if`, `#ifdef`, `#ifndef` 等條件判斷指令，符合條件的程式碼會保留，而其他的則全移除。
    - 如在當只有定義 `DEBUG` 時，`#ifdef DEBUG ... #endif` 中的程式才會被編譯。
4. Macro Operators
    - 處理 `#` 與 `##` 等巨集運算符，`#` 用於字串化，`##` 用於拼接識別字。
    - 如 `#define STR(n) #n` 會生成 `"n"`。
5. Predefined Macros
    - 展開預定義巨集，如 `__FILE__`, `__LINE__`, `__DATE__` 等，直接替換為實際內容（在預處理階段展開）。
6. Undefinition
    - 處理 `#undef` 指令，把已定義的巨集移除。
    - `#undef PI` 會移除先前定義的 `PI`。
7. Others
    - 處理如 `#error`（強制產生錯誤）, `#pragma`（控制編譯器行為）, `#line`（設定行、檔名資訊等）。

### 2. Compilation

範例指令：`g++ -S main.i -o main.s`

Compilation 編譯為 C / C++ 編譯流程中將預處理後的 Source Code 轉換成組合語言（Assembly Language）的階段，主要在做是檢查語法、語意這些東西，然後會把高階語言翻譯成符合 CPU 架構的組合語言程式碼。

用簡單幾句話來說就是把 C/C++ Source Code 翻譯成組合語言（assembly code），順便檢查語法、型態等錯誤，最後會得到一個 `.s` 檔（組合語言）。

以下是 Compilation 的步驟：

1. Syntax Analysis（語法分析）：
    編譯器會根據語法規則檢查程式碼結構是否正確，如括號配對、關鍵字使用、語句結構等。

2. Semantic Analysis（語意分析）：
    如未宣告變數、類型不匹配、不可操作之值運算都會在這檢查。

3. Intermediate Representation, IR（中介碼）：
    編譯器會將高階語言翻譯成一種介於 Source Code 和 Assembly Language 之間的中間語言表示，以便後續優化與產生組合語言代碼。

4. Optimization（優化）：
    編譯器會試著去改寫中介碼的程式碼，提升執行效率、減少不必要的運算。

5. Assembly Code Generation：
    最後編譯器會從中介碼轉換成組合語言，這時候生成的組合語言會被輸出成 `.s` 檔。

### 3. Assembly

範例指令：`g++ -c main.cpp -o main.o`

Assembly 組譯階段為 C/C++ 編譯流程中將組合語言（`.s` 檔）轉換成目標檔（object file，通常副檔名為 `.o` 或 `.obj`）的階段，目標檔是機器語言的二進位指令，可供連結器（Linker）使用。

這階段就只是把組合語言轉成機器碼（二進位指令），然後生成目標檔 `.o` 而已，然後電腦可以去執行這些低階指令，但還不能單獨去執行。

以下是 Assembly 的步驟：

1. 讀取組合語言程式碼：
    組譯器將從編譯階段輸出的組合語言程式碼（文字格式）讀入，內容是針對特定 CPU 架構設計的指令序列。
    
2. 指令轉換機器碼：
    把組合語言指令一一轉成其對應的二進位機器指令，轉換後的程式碼 CPU 可以執行。
    
3. （這步驟老實講我不知道叫啥）：
    組譯器會對程式中用到的符號（如變數、函數名稱）進行初步紀錄，但因為還沒去做連結的動作，具體地址會在連結階段決定。
    
4. 產生目標檔案（`.o` or `.obj`）：
    輸出二進位格式的 `.o` or `.obj` 檔，內含機器碼及符號表等等，用於後續連結器將不同目標檔合併。

### 4. Linking

範例指令：`g++ main.o math_utils.o -o main`

Linking 為 C/C++ 編譯流程中的最終階段，主要將多個 object files 和連結外部函式庫（如 `iostream`）合併成一個可執行檔（`.exe`），並將程式中所有符號（函數、變數等）解析和對應到真實的記憶體地址。

比如說 add(a, b) 函數定義在另一個 math.cpp 檔案裡面，而 main.cpp 要存取 math.cpp 裡面的 add(a, b) 像下面這樣：

```cpp=
// math.cpp
int add(int a, int b) {
    return a + b;
}
```

```cpp=
// main.cpp
#include <iostream>
using namespace std;

// 宣告 add 函數（定義在 math.cpp）
int add(int a, int b);

int main() {
    cout << add(3, 4) << endl;
    return 0;
}
```

若要呼叫這個函數，首先要知道他到底在記憶體的哪裡，才能去呼叫這個函數。

（假設編譯完成）Linker 會將 `main.o` 裡呼叫的 `add()` 符號，和 `math.o` 裡定義的 `add()` 符號連起來，然後把兩個 `.o` 檔案合併成最終的執行檔。

總之 Linker 在做的事情就是把不同的檔案組合再一起，形成一個可執行檔，這個 `.exe` 檔案就是多個檔案的整體。

總結
---

### 前置處理（Preprocessing）

處理所有以 `#` 開頭的指令，如 `#include` 展開標頭檔、`#define` 巨集替換、條件編譯等。

會輸出純淨的 C/C++ 程式碼，存於 `.i` 檔。

### 編譯（Compilation）

檢查語法與語意正確性，將程式轉換為中介碼（IR），再優化並產生組合語言程式碼。

產出 `.s` 檔（Assembly code）。

### 組譯（Assembly）

將組合語言轉換為二進位機器碼，並生成目標檔（Object file）。

產出 `.o` 或 `.obj` 檔案，包含機器碼與符號表，但還不能單獨執行。

### 連結（Linking）

將多個目標檔（`.o`）以及相關函式庫整合，解析所有符號（如函數、變數位置）。

最終輸出可執行檔（例如 `.exe`）。

### 結語

C++ 編譯流程大致上就是這樣：

`.cpp` → `.i` → `.s` → `.o` → `.exe`

參考資料
---

[C 概念 & 編譯 - HackMD](https://hackmd.io/@AlienHackMd/S1qP8EMmU)

[[Day 3] 編譯流程 | iT 邦幫忙::一起幫忙解決難題，拯救 IT 人的一天](https://ithelp.ithome.com.tw/m/articles/10320498)

[淺談 c++ 編譯到鏈結的過程 | by Alastor Wu | Medium](https://medium.com/@alastor0325/https-medium-com-alastor0325-compilation-to-linking-c07121e2803)

[C++ Compilation process - GeeksforGeeks](https://www.geeksforgeeks.org/cpp/how-to-compile-a-cpp-program-using-gcc/)