C程式設計語言已經存在很長時間了—它的權威參考資料是其創建者Kernighan和Ritchie寫的一本書[1978]。從那時起,C語言就開始被大量應用。用C語言編寫的程式和系統無處不在:個人電腦、電話、照相機、機上盒、冰箱、汽車、大型機、衛星……基本上在任何有可程式設計介面的現代設備中都能找到。
與C程式和系統的普遍存在相比,人們對C語言的認知和瞭解要少得多。即便是經驗豐富的C程式師,也會對C語言的現代演變表現出一定程度的知識缺乏。一個可能的原因是,C語言被看作一種“容易學習”的語言,它允許缺乏經驗的程式師快速地編寫或複製程式碼片段,這些程式碼片段至少看起來是在做它應該做的事情。在某種程度上,C語言並沒有激發使用者學習更高層次知識的積極性。
本書的目的是改變這種普遍的態度,所以它的內容分為4級,以反映對C語言和程式設計的熟悉程度。這種結構可能與讀者的一些習慣相違背,特別是,它將一些困難的主題(如指標)分成不同的層次,以避免過早地向讀者提供錯誤的資訊。我們稍後將更詳細地解釋本書的組織結構。
一般來說,儘管本書會提出許多普遍適用的思想(也適用於其他程式設計語言如Java、Python、Ruby、C#或C++),但本書主要討論C語言中特有的或者在用C語言程式設計時具有特殊價值的概念和實踐。
C語言的版本
正如本書的書名所提示的那樣,今天的C語言與它的創建者Kernighan和Ritchie最初設計的C語言(通常稱為K&R C)不同。特別是,它經歷了一個重要的標準化和擴展過程,現在由ISO(國際標準組織)進行推動。這導致了在1989年、1999年、2011年和2018年一系列C標準的發佈,它們通常被稱為C89、C99、C11和C17。C標準委員會做了大量工作來保證向後相容,比如用早期版本(如C89)編寫的代碼應該使用新版本的編譯器編譯成語義上等價的可執行檔。不幸的是,這種向後相容產生了我們不希望看到的副作用,即那些原本可以從新特性中獲益的項目沒有動力來更新自己的代碼庫。
在本書中,我們將主要參考JTC1/SC22/WG14[2018]中定義的C17,但是在撰寫本書時,一些編譯器並沒有完全實現這個標準。如果你想編譯本書中的示例,至少需要一個可以實現C99大部分功能的編譯器。對於將C11添加到C99所要做的修改,使用一個模擬層(比如我的宏包P99)就足夠了,該套裝軟體可在
http://p99.gforge.inria.fr上找到。
C和C++
程式設計已經成為一種非常重要的文化和經濟活動,C語言仍然是程式設計界的一個重要元素。與所有人類活動一樣,C語言的進步是由許多因素驅動的:企業或個人的利益、政治、美、邏輯、運氣、無知、自私、自我(這裡加上你的主要動機)。因此,C語言的發展不是也不可能是理想的。它存在缺陷和人為雕琢的成分,只能通過其歷史和社會背景來理解。
C語言開發背景的一個重要部分是它的姊妹語言C++的早期出現。一個常見的誤解是,C++是通過添加自己的特性而從C演化而來的。儘管這在歷史上是正確的(C++是從非常早期的C語言發展而來的),但它們在今天並不是特別相關。事實上,C和C++在30多年前就已經從一個共同的祖先中分離出來,並且從那以後一直在獨立地發展。但是這兩種語言的演變並不是孤立發生的,多年來,它們一直在交流和採納彼此的理念。一些新的特性,比如最近添加的原子性和執行緒,是在C和C++標準委員會的密切協作下設計的。
儘管如此,C和C++仍然有許多不同之處,而且本書中所講的全部內容都是關於C的,而不是C++。書中所給出的許多代碼示例甚至不能用C++編譯器編譯。因此我們不應該把這兩種語言的起源混為一談。
要點A C和C++是不同的:不要將它們混淆。
注意,當你閱讀本書的時候,你會遇到很多如上所示的要點。這些要點總結了特性、規則、建議等。在本書的末尾有一個包含了這些要點的列表,你可以把它作為一個備忘單。
要求為了能夠從本書中獲益,你需要滿足一些基本要求。如果你對其中任何一個不確定,請先獲取或學習它們;否則,你可能會浪費很多時間。
首先,如果不練習,你就無法學習一門程式設計語言,所以你必須有一個適當的程式設計環境(通常是在PC或筆記型電腦上),你必須在一定程度上掌握它。這個環境可以是集成的(一個IDE)或者是一組獨立的實用程式。平臺提供的內容千差萬別,因此很難給出具體建議。在類似於UNIX的環境(如Linux和蘋果的macOS)中,你可以找到諸如emacs和vim之類的編輯器,以及諸如c99、gcc和clang之類的編譯器。
你必須能夠執行以下操作:
1. 流覽檔案系統。電腦上的檔案系統通常按層次結構組織在目錄中。你必須能夠流覽它們來查找和操作檔。
2. 編輯程式文本。這與在文書處理環境中編輯字母不同。你的環境、編輯器或它所調用的任何東西都應該對程式設計語言C有基本的理解能力。你會看到,如果你打開一個C檔(副檔名通常為.C),它可能會突出顯示一些關鍵字,或者説明你根據{}的嵌套來縮進代碼。
3. 執行程式。你在這裡看到的程式一開始是非常基礎的,不會提供任何圖形功能。它們需要在命令列中啟動。編譯器就是這樣一個例子。在像UNIX這樣的環境中,命令列通常被稱為shell,其在控制台或終端上啟動。
4. 編譯器文本。有些環境提供用於編譯的功能表按鈕或鍵盤快速鍵。另一種方法是在終端的命令列中啟動編譯器。這個編譯器必須遵照最新的標準,不要把時間浪費在不適宜的編譯器上。
如果你以前從未編寫過程式,本書學起來會很難。瞭解以下內容會有所説明:Basic、C(歷史版本)、C++、Fortran、R、bash、JavaScript、Java、MATLAB、Perl、Python、Scilab等。但是,你可能有一些其他的程式設計經驗, 甚至可能沒有注意到。許多技術規範實際上是用某種專用的語言編寫的,可以作為一種類比,例如,用於Web頁面的HTML和用於文檔格式化的LaTeX。
你應該知道以下概念,儘管它們在C語言中的確切含義可能與你所學環境中的有所不同:
1. 變數—保存值的命名實體。
2. 條件句—在一個精確的條件下做某事(或不做某事)。
3. 迴圈—按一定的次數(或者直到滿足某個條件為止)重複做某事。
練習和挑戰
在本書中,你將看到一些練習,這些練習是為了讓你思考所討論的概念。最好在閱讀本書時完成練習。還有一類叫作“挑戰”。這些通常要求更高。你需要做一些研究,甚至要瞭解它們是什麼,解決方案不會自己出現:這需要努力。完成挑戰要花很多的時間,有時要幾個小時甚至幾天,這取決於你對工作的滿意程度。這些挑戰所涉及的主題來自我個人對“有趣問題”的偏好,這些問題來自我個人的經歷。如果在學習或工作中有其他問題或涉及相同領域的專案,你應該也可以把它們做得同樣好。最重要的是要訓練自己,首先從其他地方尋求幫助和想法,然後親自動手把事情做好。你只有跳進水裡才能學會游泳。
本書結構
本書按級別組織,編號從0到3。
第0級“邂逅”總結使用C語言進行程式設計的基礎知識。它的主要作用是提醒你我們所提到的主要概念,並使你熟悉C應用的特殊詞彙和觀點。最後,即使你在C語言程式設計方面沒有太多的經驗,你應該也能夠理解簡單的C語言程式的結構,並可以開始編寫自己的程式。
第1級“相識”詳細描述大多數主要概念和特性,如控制結構、資料類型、操作符和函數。它應該能讓你更深入地瞭解運行程式時所發生的事情。這些知識對於演算法入門課程和該級別的其他工作來說應該足夠了,但值得注意的是指標還沒有完全引入。
第2級“相知”深入C語言的核心。它完全解釋了指標,説明你熟悉C語言的記憶體模型,並使你能夠理解C語言的大部分庫函數介面。完成這一級別應該使你能夠專業地編寫C代碼。因此,本級別首先對C程式的編寫和組織進行了必要的討論。我個人認為,任何從工程學院畢業、主修電腦科學或C語言程式設計的人都能達到這個水準。不要滿足於比這更低的水準。
第3級“深入”詳細介紹特定主題,如性能、可重入性、原子性、執行緒和泛類型程式設計。當你在現實世界中遇到這些問題的時候,你可能會發現這裡的內容是最好的。作為一個整體,它們對於結束討論並向你提供C語言方面的全部專業知識是必要的。任何在C語言方面具有多年專業程式設計經驗的人,或者使用C語言作為主要程式設計語言的軟體專案負責人,都應該達到這個水準。