跳轉到內容

C 程式設計/編譯基礎

來自華夏公益教科書

在介紹了 C 程式設計的基本概念後,我們現在可以簡要討論一下編譯的過程。

像任何程式語言一樣,C 本身對 微處理器 來說是完全不可理解的。它的目的是為人類提供一種直觀的方式來提供指令,這些指令可以很容易地轉換為微處理器可以理解的機器程式碼。編譯器就是將我們人類可讀的原始碼翻譯成機器程式碼的工具。

對於程式設計新手來說,這似乎很簡單。一個簡單的編譯器可能會讀取每個原始檔,將所有內容翻譯成機器程式碼,然後輸出可執行檔案。這可能行得通,但有兩個嚴重的問題。首先,對於一個大型專案,計算機可能沒有足夠的記憶體來一次讀取所有原始碼。其次,如果你對一個原始檔進行修改,你將不得不重新編譯整個應用程式。

為了解決這些問題,編譯器將工作分解成幾個步驟。對於每個原始檔(每個.c檔案),編譯器讀取檔案,讀取它透過#include指令引用的檔案,並將它們翻譯成機器程式碼。這將產生一個“目標檔案”(.o)。在所有目標檔案建立之後,一個“連結器”程式會收集所有目標檔案並寫入實際的可執行程式。這樣,如果你更改一個原始檔,就只需要重新編譯該檔案,然後重新連結應用程式。

在不深入細節的情況下,瞭解編譯過程的表面理解是有益的。

預處理器

[編輯 | 編輯原始碼]

預處理器提供了包含所謂的標頭檔案、宏擴充套件、條件編譯和行控制的能力。這些功能可以透過在程式碼中插入相應的預處理器 指令 來訪問。在編譯原始碼之前,一個特殊的程式,稱為預處理器,會掃描原始碼中的標記,或特殊字串,並根據特定的規則將它們替換成其他字串或程式碼。C 預處理器從技術上來說不是 C 語言的一部分,而是你的編譯器軟體提供的工具。

所有預處理器指令都以井號 (#) 開頭。你可以在 Hello world 程式 中看到一個預處理器指令。示例

 #include <stdio.h>

此指令會導致 stdio 標頭檔案包含到你的程式中。其他指令,如#pragma,控制編譯器設定和宏。預處理階段的結果是一個文字字串。你可以將預處理器視為一個非互動式文字編輯器,它會修改你的程式碼以使其準備好進行編譯。預處理器指令的語言與 C 的語法無關,因此 C 預處理器也可以獨立地用於處理其他型別的文字檔案。

語法檢查

[編輯 | 編輯原始碼]

此步驟確保程式碼有效,並將順序排列成一個可執行程式。在大多數編譯器下,你可能會收到訊息或警告,指示你的程式中可能存在問題(例如,條件語句 始終為真或為假等等)。

當在程式中檢測到錯誤時,編譯器通常會報告阻止編譯的檔名和行號。

目的碼

[編輯 | 編輯原始碼]

編譯器會生成原始碼的機器程式碼等價物,可以將其連結到最終程式中。此時,程式碼本身不能執行,因為它需要連結才能執行。

在討論完基礎知識之後,需要注意的是,編譯是“單行道”。也就是說,將 C 原始檔編譯成機器程式碼很容易,但“反編譯”(將機器程式碼轉換成建立它的 C 原始碼)則不然。C 的反編譯器確實存在,但它們建立的程式碼難以理解,只有在 逆向工程 中才有用。

連結透過整合庫和程式碼將不同的目標檔案組合成一個完整的程式,並生成 可執行程式。連結由連結器程式執行,該程式通常是編譯器套件的一部分。

此階段常見的錯誤是缺少或重複的函式。

自動化

[編輯 | 編輯原始碼]

對於大型 C 專案,許多程式設計師選擇自動化編譯,既是為了減少使用者互動需求,也是為了透過僅重新編譯修改過的檔案來加快過程。

大多數整合開發環境 (IDE) 都有某種專案管理功能,這使得這種自動化變得非常容易。但是,專案管理檔案通常只能由使用相同整合開發環境的使用者使用,因此任何希望修改專案的人都需要使用相同的 IDE。

在類 UNIX 系統上,make 和 Makefile 通常用於實現相同的目的。make 是一種傳統且靈活的方法,在大多數 Unix 和 GNU 發行版上都作為標準開發工具之一提供。

Makefile 已透過 GNU Autotools 擴充套件,由 AutomakeAutoconf 組成,用於在多種型別的機器上使軟體可編譯、可測試、可翻譯和可配置。Automake 和 Autoconf 在各自的手冊中有詳細說明。

Autotools 通常被認為很複雜,並且已經開發出各種更簡單的構建系統。現在,GNOME 專案 的許多元件都使用宣告式的 Meson 構建系統,它不太靈活,而是專注於以簡單的方式提供構建系統中最常用的功能。C 語言編寫的程式的其他流行構建系統包括 CMakeWaf.

安裝 GCC 後,可以使用已寫入但尚未編譯的 c 原始檔列表呼叫它。例如,如果檔案 main.c 包含 myfun.h 中描述並在 myfun_a.c 和 myfun_b.c 中實現的函式,那麼只要編寫以下命令即可

 gcc   main.c myfun_a.c myfun_b.c 

myfun.h 包含在 main.c 中,但如果它在單獨的標頭檔案目錄中,那麼可以在“-I”開關之後列出該目錄。

在更大的程式中,Makefile 和 GNU make 程式可以將 c 檔案編譯成以 .o 為字尾的中間檔案,這些檔案可以由 GCC 進行連結。

如何編譯每個目標檔案通常在 Makefile 中描述,目標檔案作為標籤以冒號結尾,後面跟著兩個空格(製表符通常會導致問題),後面跟著一個其他檔案的列表,這些檔案是依賴項,例如 .c 檔案和在另一節中編譯的 .o 檔案,並在下一行,呼叫所需的 GCC。

鍵入man makeinfo make通常會提供有關如何使用 make 以及 GCC 的資訊。

儘管 GCC 有許多選項開關,但常用的一個選項是 -g,它用於生成供 gdb 除錯使用的除錯資訊,以便 gdb 在機器程式碼程式的逐行執行過程中顯示原始碼。gdb 具有一個“h”命令,它顯示了 gdb 的功能,通常透過“gdb a.out”啟動,其中 a.out 是由 GCC 編譯生成的匿名可執行機器程式碼檔案。


華夏公益教科書