C語言編譯過程



C 是一種編譯型語言。與解釋型語言相比,編譯型語言執行速度更快。可以使用不同的編譯器產品來編譯 C 程式,例如 GCC、Clang、MSVC 等。本章將解釋使用 GCC 編譯器編譯 C 程式時後臺發生的情況。

編譯C程式

一系列由 1 和 0 組成的二進位制指令被稱為機器碼。C、C++、Java 等高階程式語言包含更接近人類語言(如英語)的關鍵字。因此,用 C(或任何其他高階語言)編寫的程式需要轉換為其等效的機器碼。這個過程稱為編譯

請注意,機器碼是特定於硬體架構和作業系統的。換句話說,在 Windows 作業系統的計算機上編譯的特定 C 程式的機器碼與使用 Linux 作業系統的另一臺計算機不相容。因此,我們必須使用適合目標作業系統的編譯器。

Compilation

C語言編譯過程步驟

在本教程中,我們將使用 gcc(代表 GNU 編譯器集合)。GNU 專案是由 Richard Stallman 發起的自由軟體專案,允許開發人員免費訪問強大的工具。

gcc 編譯器支援多種程式語言,包括 C 語言。為了使用它,我們應該安裝與其目標計算機相容的版本。

編譯過程包含四個不同的步驟:

  • 預處理
  • 編譯
  • 彙編
  • 連結

下圖說明了編譯過程。

Compilation Process

示例

為了理解這個過程,讓我們考慮以下 C 語言原始碼 (main.c):

#include <stdio.h>

int main(){
   
   /* my first program in C */
   
   printf("Hello World! \n");

   return 0;
}

輸出

執行程式碼並檢查其輸出:

Hello World!

“.c” 是一個副檔名,通常表示該檔案是用 C 編寫的。第一行是預處理指令#include,它告訴編譯器包含stdio.h標頭檔案。/**/ 之間的文字是註釋,這些註釋對於文件目的很有用。

程式的入口點是main() 函式。這意味著程式將首先執行此函式塊內的語句。在此給定的程式程式碼中,只有兩條語句:一條語句將在終端列印句子“Hello World”,另一條語句告訴程式如果它正確退出或結束則“返回 0”。因此,一旦我們編譯它,如果我們執行此程式,我們將只看到顯示的短語“Hello World”。

C編譯過程內部發生了什麼?

為了使我們的“main.c”程式碼可執行,我們需要輸入命令“gcc main.c”,編譯過程將經歷其包含的四個步驟。

步驟 1:預處理

預處理器執行以下操作:

  • 它刪除原始檔中的所有註釋。
  • 它包含標頭檔案(.h)的程式碼,標頭檔案包含 C 函式宣告和宏定義。
  • 它用其值替換所有宏(已命名的程式碼片段)。

此步驟的輸出將儲存在副檔名為“.i”的檔案中,因此這裡將儲存在“main.i”中。

為了在此步驟之後停止編譯,我們可以對原始檔使用 gcc 命令的“-E”選項,然後按 Enter。

gcc -E main.c

步驟 2:編譯

編譯器從預處理檔案生成 IR 程式碼(中間表示),因此這將生成一個“.s”檔案。也就是說,其他編譯器可能在此編譯步驟中生成彙編程式碼。

我們可以使用 gcc 命令的“-S”選項在該步驟後停止,然後按 Enter。

gcc -S main.c

main.s 檔案應如下所示:

.file	"helloworld.c"
   .text
   .def	__main;	.scl	2;	.type	32;	.endef
   .section .rdata,"dr"
.LC0:
   .ascii "Hello, World! \0"
   .text
   .globl	main
   .def	main;	.scl	2;	.type	32;	.endef
   .seh_proc	main
main:
   pushq	%rbp
   .seh_pushreg	%rbp
   movq	%rsp, %rbp
   .seh_setframe	%rbp, 0
   subq	$32, %rsp
   .seh_stackalloc	32
   .seh_endprologue
   call	__main
   leaq	.LC0(%rip), %rcx
   call	puts
   movl	$0, %eax
   addq	$32, %rsp
   popq	%rbp
   ret
   .seh_endproc
   .ident	"GCC: (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0"
   .def	puts;	.scl	2;	.type	32;	.endef

步驟 3:彙編

彙編器獲取 IR 程式碼並將其轉換為機器語言程式碼(即二進位制程式碼)。這將生成一個以“.o”結尾的檔案。

我們可以透過使用 gcc 命令的“-c”選項並在之後按 Enter 來在此步驟之後停止編譯過程。

請注意,“main.o”檔案不是文字檔案,因此當您使用文字編輯器開啟此檔案時,其內容將不可讀。

步驟 4:連結

連結器建立最終的可執行二進位制檔案。它將所有原始檔的目的碼連結在一起。連結器知道在哪裡查詢靜態庫動態庫中的函式定義。

靜態庫是連結器將所有使用的庫函式的副本複製到可執行檔案的結果。動態庫中的程式碼不會被完全複製,只有庫的名稱會被放在二進位制檔案中。

預設情況下,在此第四個也是最後一步之後,也就是在您鍵入完整的“gcc main.c”命令沒有任何選項時,編譯器將建立一個名為main.out(或在 Windows 系統中為main.exe)的可執行程式,我們可以從命令列執行它。

我們還可以透過向 gcc 命令新增“-o”選項來建立我們想要名稱的可執行程式,該選項放置在我們正在編譯的檔案或檔案的名稱之後。

gcc main.c -o hello.out

因此,現在我們可以鍵入“./hello.out”(如果您沒有使用“-o”選項)或“./hello”來執行編譯後的程式碼。輸出將是“Hello World”,之後將再次出現 shell 提示符。

廣告