本節書摘來自異步社群《c primer plus(第6版)中文版》一書中的第1章,第1.8節,作者 傅道坤,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。
生成程式的具體過程因計算機環境而異。c是可移植性語言,是以可以在許多環境中使用,包括unix、linux、ms-dos(一些人仍在使用)、windows和macintosh os。有些産品會随着時間的推移發生演變或被取代,本書無法涵蓋所有環境。
首先,來看看許多c環境(包括上面提到的5種環境)共有的一些方面。雖然不必詳細了解計算機内部如何運作c程式,但是,了解一下程式設計機制不僅能豐富程式設計相關的背景知識,還有助于了解為何要經過一些特殊的步驟才能得到c程式。
用c語言編寫程式時,編寫的内容被儲存在文本檔案中,該檔案被稱為源代碼檔案(source code file)。大部分c系統,包括之前提到的,都要求檔案名以.c結尾(如,wordcount.c和budget.c)。在檔案名中,點号(.)前面的部分稱為基本名(basename),點号後面的部分稱為擴充名(extension)。是以,budget是基本名,c是擴充名。基本名與擴充名的組合(budget.c)就是檔案名。檔案名應該滿足特定計算機作業系統的特殊要求。例如,ms-dos是ibm pc及其相容機的作業系統,比較老舊,它要求基本名不能超過8個字元。是以,剛才提到的檔案名wordcount.c就是無效的dos檔案名。有些unix系統限制整個檔案名(包括擴充名)不超過14個字元,而有些unix系統則允許使用更長的檔案名,最多255個字元。linux、windows和macintosh os都允許使用長檔案名。
接下來,我們來看一下具體的應用,假設有一個名為concrete.c的源檔案,其中的c源代碼如程式清單1.2所示。
程式清單1.2 c程式
如果看不懂程式清單1.2中的代碼,不用擔心,我們将在第2章學習相關知識。
c程式設計的基本政策是,用程式把源代碼檔案轉換為可執行檔案(其中包含可直接運作的機器語言代碼)。典型的c實作通過編譯和連結兩個步驟來完成這一過程。編譯器把源代碼轉換成中間代碼,連結器把中間代碼和其他代碼合并,生成可執行檔案。c使用這種分而治之的方法友善對程式進行子產品化,可以獨立編譯單獨的子產品,稍後再用連結器合并已編譯的子產品。通過這種方式,如果隻更改某個子產品,不必是以重新編譯其他子產品。另外,連結器還将你編寫的程式和預編譯的庫代碼合并。
中間檔案有多種形式。我們在這裡描述的是最普遍的一種形式,即把源代碼轉換為機器語言代碼,并把結果放在目标代碼檔案(或簡稱目标檔案)中(這裡假設源代碼隻有一個檔案)。雖然目标檔案中包含機器語言代碼,但是并不能直接運作該檔案。因為目标檔案中儲存的是編譯器翻譯的源代碼,這還不是一個完整的程式。
目标代碼檔案缺失啟動代碼(startup code)。啟動代碼充當着程式和作業系統之間的接口。例如,可以在ms windows或linux系統下運作ibm pc相容機。這兩種情況所使用的硬體相同,是以目标代碼相同,但是windows和linux所需的啟動代碼不同,因為這些系統處理程式的方式不同。
目标代碼還缺少庫函數。幾乎所有的c程式都要使用c标準庫中的函數。例如,concrete.c中就使用了printf()函數。目标代碼檔案并不包含該函數的代碼,它隻包含了使用printf()函數的指令。printf()函數真正的代碼儲存在另一個被稱為庫的檔案中。庫檔案中有許多函數的目标代碼。
連結器的作用是,把你編寫的目标代碼、系統的标準啟動代碼和庫代碼這3部分合并成一個檔案,即可執行檔案。對于庫代碼,連結器隻會把程式中要用到的庫函數代碼提取出來(見圖1.4)。

圖1.4 編譯器和連結器
簡而言之,目标檔案和可執行檔案都由機器語言指令組成的。然而,目标檔案中隻包含編譯器為你編寫的代碼翻譯的機器語言代碼,可執行檔案中還包含你編寫的程式中使用的庫函數和啟動代碼的機器代碼。
在有些系統中,必須分别運作編譯程式和連結程式,而在另一些系統中,編譯器會自動啟動連結器,使用者隻需給出編譯指令即可。
接下來,了解一些具體的系統。
由于c語言因unix系統而生,也是以而流行,是以我們從unix系統開始(注意:我們提到的unix還包含其他系統,如freebsd,它是unix的一個分支,但是由于法律原因不使用該名稱)。
1.在unix系統上編輯
unix c沒有自己的編輯器,但是可以使用通用的unix編輯器,如emacs、jove、vi或x window system文本編輯器。
作為程式員,要負責輸入正确的程式和為儲存該程式的檔案起一個合适的檔案名。如前所述,檔案名應該以.c結尾。注意,unix區分大小寫。是以,budget.c、budget.c和budget.c是3個不同但都有效的c源檔案名。但是budget.c是無效檔案名,因為該名稱的擴充名使用了大寫c而不是小寫c。
假設我們在vi編譯器中編寫了下面的程式,并将其儲存在inform.c檔案中:
以上文本就是源代碼,inform.c是源檔案。注意,源檔案是整個編譯過程的開始,不是結束。
2.在unix系統上編譯
雖然在我們看來,程式完美無缺,但是對計算機而言,這是一堆亂碼。計算機不明白#include和printf是什麼(也許你現在也不明白,但是學到後面就會明白,而計算機卻不會)。如前所述,我們需要編譯器将我們編寫的代碼(源代碼)翻譯成計算機能看懂的代碼(機器代碼)。最後生成的可執行檔案中包含計算機要完成任務所需的所有機器代碼。
以前,unix c編譯器要調用語言定義的cc指令。但是,它沒有跟上标準發展的腳步,已經退出了曆史舞台。但是,unix系統提供的c編譯器通常來自一些其他源,然後以cc指令作為編譯器的别名。是以,雖然在不同的系統中會調用不同的編譯器,但使用者仍可以繼續使用相同的指令。
編譯inform.c,要輸入以下指令:
幾秒鐘後,會傳回unix的提示,告訴使用者任務已完成。如果程式編寫錯誤,你可能會看到警告或錯誤消息,但我們先假設編寫的程式完全正确(如果編譯器報告void的錯誤,說明你的系統未更新成ansi c編譯器,隻需删除void即可)。如果使用ls指令列出檔案,會發現有一個a.out檔案(見圖1.5)。該檔案是包含已翻譯(或已編譯)程式的可執行檔案。要運作該檔案,隻需輸入:
輸出内容如下:
圖1.5 用unix準備c程式
如果要儲存可執行檔案(a.out),應該把它重命名。否則,該檔案會被下一次編譯程式時生成的新a.out檔案替換。
如何處理目标代碼?c編譯器會建立一個與源代碼基本名相同的目标代碼檔案,但是其擴充名是.o。在該例中,目标代碼檔案是inform.o。然而,卻找不到這個檔案,因為一旦連結器生成了完整的可執行程式,就會将其删除。如果原始程式有多個源代碼檔案,則保留目标代碼檔案。學到後面多檔案程式時,你會明白到這樣做的好處。
gnu項目始于1987年,是一個開發大量免費unix軟體的集合(gnu的意思是“gnu’s not unix”,即gnu不是unix)。gnu編譯器集合(也被稱為gcc,其中包含gcc c編譯器)是該項目的産品之一。gcc在一個指導委員會的帶領下,持續不斷地開發,它的c編譯器緊跟c标準的改動。gcc有各種版本以适應不同的硬體平台和作業系統,包括unix、linux和windows。用gcc指令便可調用gcc c編譯器。許多使用gcc的系統都用cc作為gcc的别名。
llvm項目成為cc的另一個替代品。該項目是與編譯器相關的開源軟體集合,始于伊利諾伊大學的2000份研究項目。它的clang編譯器處理c代碼,可以通過clang調用。有多種版本供不同的平台使用,包括linux。2012年,clang成為freebsd的預設c編譯器。clang也對最新的c标準支援得很好。
gnu和llvm都可以使用-v選項來顯示版本資訊,是以各系統都使用cc别名來代替gcc或clang指令。以下組合:
顯示你所使用的編譯器及其版本。
gcc和clang指令都可以根據不同的版本選擇運作時選項來調用不同c标準。
第1行調用c99标準,第2行調用gcc接受c11之前的草案标準,第3行調用gcc接受的c11标準版本。clang編譯器在這一點上用法與gcc相同。
linux是一個開源、流行、類似于unix的作業系統,可在不同平台(包括pc和mac)上運作。在linux中準備c程式與在unix系統中幾乎一樣,不同的是要使用gnu提供的gcc公共域c編譯器。編譯指令類似于:
注意,在安裝linux時,可選擇是否安裝gcc。如果之前沒有安裝gcc,則必須安裝。通常,安裝過程會将cc作為gcc的别名,是以可以在指令行中使用cc來代替gcc。
c編譯器不是标準windows軟體包的一部分,是以需要從别處擷取并安裝c編譯器。可以從網際網路免費下載下傳cygwin和mingw,這樣便可在pc上通過指令行使用gcc編譯器。cygwin在自己的視窗運作,模仿linux指令行環境,有一行指令提示。mingw在windows的指令提示模式中運作。這和gcc的最新版本一樣,支援c99和c11最新的一些功能。borland的c++編譯器5.5也可以免費下載下傳,支援c90。
源代碼檔案應該是文本檔案,不是字處理器檔案(字處理器檔案包含許多額外的資訊,如字型和格式等)。是以,要使用文本編輯器(如,windows notepad)來編輯源代碼。如果使用字處理器,要以文本模式另存檔案。源代碼檔案的擴充名應該是.c。一些字處理器會為文本檔案自動添加.txt擴充名。如果出現這種情況,要更改檔案名,把txt替換成c。
通常,c編譯器生成的中間目标代碼檔案的擴充名是.obj(也可能是其他擴充名)。與unix編譯器不同,這些編譯器在完成編譯後通常不會删除這些中間檔案。有些編譯器生成帶.asm擴充名的彙編語言檔案,而有些編譯器則使用自己特有的格式。
一些編譯器在編譯後會自動運作連結器,另一些要求使用者手動運作連結器。在可執行檔案中連結的結果是,在原始的源代碼基本名後面加上.exe擴充名。例如,編譯和連結concrete.c源代碼檔案,生成的是concrete.exe檔案。可以在指令行輸入基本名來運作該程式:
許多供應商(包括微軟、embarcadero、digital mars)都提供windows下的內建開發環境,或稱為ide(目前,大多數ide都是c和c++結合的編譯器)。可以免費下載下傳的ide有microsoft visual studio express和pelles c。利用內建開發環境可以快速開發c程式。關鍵是,這些ide都内置了用于編寫c程式的編輯器。這類內建開發環境都提供了各種菜單(如,命名、儲存源代碼檔案、編譯程式、運作程式等),使用者不用離開ide就能順利編寫、編譯和運作程式。如果編譯器發現錯誤,會傳回編輯器中,标出有錯誤的行号,并簡單描述情況。
初次接觸windows ide可能會望而生畏,因為它提供了多種目标(target),即運作程式的多種環境。例如,ide提供了32位windows程式、64位windows程式、動态連結庫檔案(dll)等。許多目标都涉及windows圖形界面。要管理這些(及其他)選擇,通常要先建立一個項目(project),以便稍後在其中添加待使用的源代碼檔案名。不同的産品具體步驟不同。一般而言,首先使用【檔案】菜單或【項目】菜單建立一個項目。選擇正确的項目形式非常重要。本書中的例子都是一般示例,針對在簡單的指令行環境中運作而設計。windows ide提供多種選擇以滿足使用者的不同需求。例如,microsoft visual studio提供【win32控制台應用程式】選項。對于其他系統,查找一個諸如【dos exe】、【console】或【character mode】的可執行選項。選擇這些模式後,将在一個類控制台視窗中運作可執行程式。選擇好正确的項目類型後,使用ide的菜單打開一個新的源代碼檔案。對于大多數産品而言,使用【檔案】菜單就能完成。你可能需要其他步驟将源檔案添加到項目中。
通常,windows ide既可處理c也可處理c++,是以要指定待處理的程式是c還是c++。有些産品用項目類型來區分兩者,有些産品(如,microsoft visual c++)用.c檔案擴充名來指明使用c而不是c++。當然,大多數c程式也可以作為c++程式運作。欲了解c和c++的差別,請參閱參考資料ix。
你可能會遇到一個問題:在程式執行完畢後,執行程式的視窗立即消失。如果不希望出現這種情況,可以讓程式暫停,直到按下enter鍵,視窗才消失。要實作這種效果,可以在程式的最後(return這行代碼之前)添加下面一行代碼:
該行讀取一次鍵的按下,是以程式在使用者按下enter鍵之前會暫停。有時根據程式的需要,可能還需要一個擊鍵等待。這種情況下,必須用兩次getchar():
例如,程式在最後提示使用者輸入體重。使用者鍵入體重後,按下enter鍵以輸入資料。程式将讀取體重,第1個getchar()讀取enter鍵,第2個getchar()會導緻程式暫停,直至使用者再次按下enter鍵。如果你現在不知所雲,沒關系,在學完c輸出後就會明白。到時,我們會提醒讀者使用這種方法。
雖然許多ide在使用上大體一緻,但是細節上有所不同。就一個産品的系列而言,不同版本也是如此。要經過一段時間的實踐,才會熟悉編譯器的工作方式。必要時,還需閱讀使用手冊或網上教程。
在windows軟體開發中,microsoft visual studio及其免費版本microsoft visual studio express都久負盛名,它們與c标準的關系也很重要。然而,微軟鼓勵程式員從c轉向c++和c#。雖然visual studio支援c89/90,但是到目前為止,它隻選擇性地支援那些在c++新特性中能找到的c标準(如,long long類型)。而且,自2012版本起,visual studio不再把c作為項目類型的選項。盡管如此,本書中的絕大多數程式仍可用visual studio來編譯。在建立項目時,選擇c++選項,然後選擇【win32控制台應用程式】,在應用設定中選擇【空項目】。幾乎所有的c程式都能與c++程式相容。是以,本書中的絕大多數c程式都可作為c++程式運作。或者,在選擇c++選項後,将預設的源檔案擴充名.cpp替換成.c,編譯器便會使用c語言的規則代替c++。
許多linux發行版都可以安裝在windows系統中,以建立雙系統。一些存儲器會為linux系統預留白間,以便可以啟動windows或linux。可以在windows系統中運作linux程式,或在linux系統中運作windows程式。不能通過windows系統通路linux檔案,但是可以通過linux系統通路windows文檔。
目前,蘋果免費提供xcode開發系統下載下傳(過去,它有時免費,有時付費)。它允許使用者選擇不同的程式設計語言,包括c語言。
xcode憑借可處理多種程式設計語言的能力,可用于多平台,開發超大型的項目。但是,首先要學會如何編寫簡單的c程式。在xcode 4.6中,通過【file】菜單選擇【new project】,然後選擇【os x application command line tool】,接着輸入産品名并選擇c類型。xcode使用clang或gcc c編譯器來編譯c代碼,它以前預設使用gcc,但是現在預設使用clang。可以設定選擇使用哪一個編譯器和哪一套c标準(因為許可方面的事宜,xcode中clang的版本比gcc的版本要新)。
unix系統内置mac os x,終端工具打開的視窗是讓使用者在unix指令行環境中運作程式。蘋果在标準軟體包中不提供指令行編譯器,但是,如果下載下傳了xcode,還可以下載下傳可選的指令行工具,這樣就可以使用clang和gcc指令在指令行模式中編譯。