編譯流程
在完成原始碼後,要經過一個名為「編譯」的系列行為,讓原始碼成為可執行檔。
註:此處的「編譯」指的是前置處理、組譯、編譯及連結四項動作的集合,並不單指編譯(Compile)這項動作。
前置處理(Preprocess)
原始碼會先經過前置處理手續,用意是將 前置處理器(Preprocessor)
中定義的內容進行處理,方便後續編譯流程。
關於前置處理器會在後面的章節詳細說明,在此處僅需要瞭解 #include <stdio.h>
就是前置處理器的一種即可。
在前置處理時,編譯器會去除所有前置處理器,以 #include <stdio.h>
為例,前置處理會嘗試取得 C 語言內建的 stdio.h
檔案,並將檔案內容經過前置處理後置入我們的原始碼中。
舉例而言,我們擁有上一節所撰寫的程式碼HelloWorld.c
#include <stdio.h>
int main()
{
puts("Hello World");
return 0;
}
我們可以利用 gcc
指令將 HelloWorld.c
預處理為 pre.c
gcc -o pre.c -E HelloWorld.c
註:
pre.c
為可自行設定之檔案名稱,可以變更gcc
之參數-o
後方的值決定輸出的檔案名稱
編譯(Compile)
編譯是將已經過前置處理的原始碼轉化為組合語言(Assembly)的過程。
可能一行 C 語言指令就代表著數十行的組合語言,編譯器也會在此時針對程式進行最佳化(減少記憶體空間使用量、降低時間複雜度)
我們可以利用 gcc
指令將 pre.c
編譯為 asm.s
gcc -o asm.s -S pre.c
組譯(Assemble)
一般來說,組合語言可一對一翻譯為目的程式(Object Program,內容包含 0, 1 的機器語言)。
在組譯階段就是將組合語言組譯為目的程式。
我們可以利用 gcc
指令將 asm.s
組譯為目的程式 HelloWorld.o
gcc -o HelloWorld.o -c asm.s
連結(Link)
有時候一個大型程式可能是由多個元件所組成,這些元件可能來自多個不同的目的程式。
在此時會連結多個目的程式,產生可執行檔。
此階段編譯器也可能會根據編譯指令加入部份作業系統資訊。
我們可以利用 gcc
指令將 HelloWorld.o
連結後產生可執行檔 HelloWorld.out
gcc -o HelloWorld.out HelloWorld.o
如果有多個目的程式,則可以利用下方兩種指令連結並產生可執行檔(擇一即可)
gcc -o HelloWorld.out 1.o 2.o
gcc -o HelloWorld.out *.o
簡化
本節中為了說明各編譯流程行為,故將整個編譯流程拆分為四個動作。
其實可以利用一行指令解決
gcc -o HelloWorld.out HelloWorld.c
如果有多個 .c
檔生成一個可執行檔,則可以利用下列指令
gcc -o HelloWorld.out *.c
然而,在編譯大型程式時,可能會重複利用同一個目的程式,如果每一個目的程式都在被引用時重新編譯,勢必會拖長編譯時間。
故常見的解決方案為:先將全部(或部份使用較頻繁的).c
檔前置處理、編譯及組譯為目的檔案 .o
然後再一起編譯為可執行檔。
目前原始碼的數量並不多,這樣的方法已經足夠,未來我們會用更方便的工具達成多個原始碼的自動化編譯(第十四章:Makefile)。