跳轉到內容

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 a.out”啟動,如果 a.out 是由 GCC 編譯的匿名可執行機器程式碼檔案。


華夏公益教科書