C 語言程式設計教學:編譯 C 程式碼的過程

PUBLISHED ON JUN 13, 2018 — PROGRAMMING

在先前的文章中,我們從工具的觀點來看如何編譯 C 程式碼,在本文中,我們從高階抽象的觀點來看 C 程式碼如何變成執行檔。

從程式碼變程式

如果沒有編譯器 (compiler) 或直譯器 (interpreter),程式碼只不過是有著某種特殊形式的文字檔案,透過編譯或直譯的過程,程式碼才會變成可在電腦中執行的程式。以 C 語言來說,雖然編譯看起來只是在 IDE 中按個鈕或是輸入一行指令,但內部經過許多道步驟,約略分為以下四項:

  • 前處理 (preprocessing)
  • 編譯 (compiling)
  • 組譯 (assembling)
  • 連結 (linking)

在前處理時,前處理器會將 C 程式碼中註解的部分去除、巨集 (macro) 和外部包含 (即 #include) 的部分以文字擴張的方式取代掉,這並不是實質的編譯,僅能算是文字處理。處理過的程式碼就會繼續進行下一個步驟。

接著,編譯器會進行實際的編譯行為,在這個過程中,編譯器會將抽象而高階的 C 程式碼轉換為低階而貼近電腦的組合語言程式碼 (assembly)。這個過程又經過許多步驟,我們將其寫在下一節,有興趣的讀者可以自行閱讀。

組譯器會將組語程式碼轉為相對應的機械碼,這時候的產出物為目的碼 (object code)。最後,透過連結器將目的碼和函式庫結合,就會產出執行檔 (executable),整個步驟就算完成。使用者只要將這個程式載入 (loading) 系統,就可以執行此程式。

這裡僅簡要說明編譯的過程,如果讀者想要知道更多技術面的細節,可以參考 程式設計師的自我修養:連結、載入、程式庫, 碁峰 (2009) 或是 *Advanced C and C++ Compiling, Apress (2014)*。

(選讀) 編譯和直譯流程

我們在這裡以高階的角度來看待編譯和直譯的過程,不僅僅限於 C 語言,如果讀者對這些細節沒興趣或先前沒有程式設計的經驗,可以先跳過這一節的內容也無妨。

我們先說明編譯的流程,直譯有一部分和編譯重疊,有一部分則不同,我們稍後會說明。編譯的過程就像生產線般,有一系列的工作流程 (pipeline),這個過程可分為前端 (front end) 和後端 (back end) 兩個部分;前端將程式碼 (code) 轉為中間碼 (intermediate representation),後端則將中間碼轉為機械碼 (machine code)。

前端包括以下流程:

  • 詞法分析 (lexical analysis)
  • 語法分析 (syntax analysis)
  • 語意分析 (semantic analysis)
  • 生成中間碼 (intermediate code generation)

後端包括以下流程:

  • (選擇性) 和機械碼無關的優化 (machine-independent code improvement)
  • 生成目標程式碼 (target code generation)
  • (選擇性) 和機械碼相關的優化 (machine-specific code improvement)

double n = 3.12 + 1.95; 這句來說,詞法分析會將這串文字變成一個個 token,像 3.12 就是一個 token。接著,語法分析會將