C語言編寫程序時,編寫的內容被存儲在文本文件中,該文件被稱為源代碼文件(source-code-file)。大部分C系統,包括之前提到的,都要求文件名以.c結尾(如,wordcount.c和budget.c)。在文件名中,點號(.)前面的部分稱為基本名(basename),點號后面的部分稱為擴展名(extension)。因此,budget是基本名,c是擴展名。基本名與擴展名的組合(budget.c)就是文件名。文件名應該滿足特定計算機操作系統的特殊要求。我們來看一下具體的應用,假設有一個名為concrete.c的源文件,其中的C源代碼如程序清單1.2所示。
Listing 1.2 The concrete.c Program
int main(void)
{
printf("Concrete contains gravel and cement.n");
return 0;
}
1 目標代碼文件、可執行文件和庫
C編程的基本策略是,用程序把源代碼文件轉換為可執行文件(其中包含可直接運行的機器語言代碼)。典型的C實現通過編譯和鏈接兩個步驟來完成這一過程。編譯器把源代碼轉換成中間代碼,鏈接器把中間代碼和其他代碼合并,生成可執行文件。
C使用這種分而治之的方法方便對程序進行模塊化,可以獨立編譯單獨的模塊,稍后再用鏈接器合并已編譯的模塊。通過這種方式,如果只更改某個模塊,不必因此重新編譯其他模塊。另外,鏈接器還將你編寫的程序和預編譯的庫代碼合并。中間文件有多種形式。我們在這里描述的是最普遍的一種形式,即把源代碼轉換為機器語言代碼,并把結果放在目標代碼文件(或簡稱目標文件)中(這里假設源代碼只有一個文件)。雖然目標文件中包含機器語言代碼,但是并不能直接運行該文件。因為目標文件中存儲的是編譯器翻譯的源代碼,這還不是一個完整的程序。
目標代碼文件缺失啟動代碼(startup-code)。啟動代碼充當著程序和操作系統之間的接口。
目標代碼還缺少庫函數。幾乎所有的C程序都要使用C標準庫中的函數。例如,concrete.c中就使用了printf()函數。目標代碼文件并不包含該函數的代碼,它只包含了使用printf()函數的指令。printf()函數真正的代碼存儲在另一個被稱為庫的文件中。庫文件中有許多函數的目標代碼。鏈接器的作用是,把你編寫的目標代碼、系統的標準啟動代碼和庫代碼這3部分合并成一個文件,即可執行文件。對于庫代碼,鏈接器只會把程序中要用到的庫函數代碼提取出來(見圖1)。

Figure 1 Compiler and linker.
簡而言之,目標文件和可執行文件都由機器語言指令組成的。然而,目標文件中只包含編譯器為你編寫的代碼翻譯的機器語言代碼,可執行文件中還包含你編寫的程序中使用的庫函數和啟動代碼的機器代碼。在有些系統中,必須分別運行編譯程序和鏈接程序,而在另一些系統中,編譯器會自動啟動鏈接器,用戶只需給出編譯命令即可。接下來,了解一些具體的系統。
2 GNU編譯器集合和LLVM項目
GNU和LLVM都可以使用-v選項來顯示版本信息,因此各系統都使用cc別名來代替gcc或clang命令。以下組合:
cc -v
顯示你所使用的編譯器及其版本。gcc和clang命令都可以根據不同的版本選擇運行時選項來調用不同C標準。
gcc -std=c99 inform.c
gcc -std=c1x inform.c
gcc -std=c11 inform.c
第1行調用C99標準,第2行調用GCC接受C11之前的草案標準,第3行調用GCC接受的C11標準版本。Clang編譯器在這一點上用法與GCC相同。
