簡介

前言

當我還是一個大學生的時候,總覺得 C 語言就是這樣了。但是在 10 年後我進入職場時,才發現原來我並不太認識這個語言。產業界所使用的 C 語言有許多是大學所沒有教授過的,像是 #ifdef、make、GNU 工具等等。又過了 10 年,當我研究嵌入式系統時,這個感覺又出現了,我仍然不太認識 C 語言,嵌入式系統中所使用的「記憶體映射輸出入、volatile、組合語言連接、Linker Script」等,又讓我耳目一新,我再度重新認識了 C 語言一次。然後,當我研讀 Linux 核心的程式碼時,看到 Torvalds 所使用的「鏈結串列、行程切換技巧」等,又再度讓我大為驚訝,C 語言竟然還可以這樣用。然後,當我開始研究 Google Android 手機平台的架構時,又看到了如何用 C 語言架構出網路、視窗、遊戲、瀏覽器等架構,於是我必須再度學習一次 C 語言。

當我翻閱坊間的書籍時,不禁如此想著,如果有人能直接告訴我這些 C 語言的學習歷程,那應該有多好。難道,我們真的必需花上數十年的時間去學習 C 語言,才能得到這些知識嗎?這些知識在初學者的眼中,看來簡直像是「奇技淫巧」。然而這些「奇技淫巧」,正是 C 語言為何如此強大的原因,我希望能透過這本書,告訴各位這些「奇技淫巧」,讓各位讀者不需要再像我一樣,花上二十年功夫,才能學會這些技術。

在我的眼中,C 語言就像一把鋒利的雙面刃,初出茅廬的人往往功力不夠深厚,反而將這個神兵利器往自己身上砍,因而身受重傷。但是在專家的手中,C 語言卻具有無比的威力,這種神兵利器具有「十年磨一劍、十步殺一人」的驚人力量。筆者希望能透過這本書,讓讀者能夠充分發揮 C 語言的力量,快速的掌握這個難以駕馭的神兵利器。

C 語言的由來

從 1972 年 Dennis Ritchie 在貝爾實驗室發明 C 語言至今,已經超過四十年。在這個變化快速的電腦世界裡,C 語言彷彿成了不變的避風港。四十年來,C 語言的改變並不多,而且一直都是所有作業系統底層的主力語言。近來,由於 Linux 與開放原始碼的發展,C 語言的影響力更為增強。在這裡,我不禁要問一個問題,為何 C 語言可以經過四十年而幾乎毫不改變。

C 語言很快,這或許是原因之ㄧ,但是像 Pascal 或 Fortran 等語言也幾乎與 C 語言一樣快,那又為何非 C 語言不可。但是,C 語言不只是快,還具有指標,容易與組合語言連結,具有巨集、條件式編譯、inline 函數、結構化、可以使用記憶體映射輸出入,因此可以用高階語言撰寫低階輸出入驅動程式,還有撰寫作業系統。

這些特性,讓 C 語言特別適合撰寫嵌入式系統,在某些 deeply embedded 的資源受限型嵌入式系統中,只有很小的記憶體、很慢的 CPU、而且通常沒有硬碟。這種環境有點像相當於當年 Dennis Ritchie 所面對的環境,因為他們必須在很克難的資源中,發展出夠強大的作業系統。也正是因為這種環境的淬鍊、讓 C 語言在《嵌入式系統》和《作業系統》領域都有非常優異的表現。

UNIX 正是催生 C 語言的主要動力,當年 Ken Thompson 與 Dennis Ritchie 正是為了發展 UNIX 而設計出 C 語言的,這兩人也因為 UNIX/C 的貢獻而被 ACM 授予 Turing Award 這的電腦界的諾貝爾獎。

在 1978 年,Dennis 與另一位共同作者 Brian Wilson Kernighan 合力撰寫了第一本廣為流傳的 C 語言教科書 ,而這個版本的教科書由於影響深遠,成為人手一冊的 C 語言經典,因此後來我們這個版本的 C 語言教科書簡稱為 K&R 版本,這個經典書籍中所使用的 C 語言版本也因此而被稱為 K&R 版的 C 語言,以便與後來 1988 年的 ANSI C 版本,以及 1999 年的 ISO C99 版本有所區隔。 (一個很容易誤會的點是, Ken Thompson 與 Brian Wilson Kernighan 是不同的兩個人,Ken Thompson 是發明 UNIX 與 C 語言的那個 Turing Award 得獎者,但是 Brian Wilson Kernighan 則是 C 語言書籍的作者,這兩個人的名字雖然都以 K 開頭,但是此 K 非比 K,請讀者切勿混淆)。

因此,學習 C 語言的人,如果只是將 C 當作是一般的程式語言,就會難以體會 C 語言的威力之所在,我們必須進入嵌入式與作業系統的領域,才能體會 C 語言的優點。一但您能夠體會這些優點,C 語言將不再僅僅是一個普通的語言,您也將能體會為何 C 語言會經歷四十年而不墬。然後,您也才能發揮 C 語言的能力,並且體會這些設計背後的優點與缺點。

C 語言的優缺點

C 語言並非沒有缺點的,實際上,C 語言的缺點非常的多,多到可以用罄竹難書來形容。舉例而言,用 C 語言寫程式很容易有 bug,特別是在記憶體分配與回收這部份更是如此。C 語言沒有自動記憶体回收機制,沒有垃圾收集功能,因此常常導致忘記釋放記憶體,或者將同一個記憶體釋放數次,因而造成錯誤。C 語言的字串很原始,使用起來非常不方便。C 語言的標準函式庫甚至沒有基本的資料結構,像是陣列、串列、堆疊、字典等相關結構的函式庫。C 語言的條件式編譯讓程式看起來很冗長,使用標頭檔 *.h 讓你必需重複撰寫函數表頭,浪費許多時間。更糟的是,由於 C 語言的標準函式庫很小,因此在不同的平台上,每個廠商都實作出完全不同的函式庫,這導致 C 語言的程式難以跨越平台執行,您必須位每個平台打造一份程式,而不像 Java 那樣可以 Write Once,Run Anywhere。

但是,即便有了這麼多的缺點,C 語言仍然歷經四十年而不衰,這又是為甚麼呢?

每個 C 語言的缺點,幾乎都是伴隨著其優點而來的,C 語言的記憶體難以管理,是因為 C 語言具有強大的指標功能。字串函數很原始,是為了讓您可以使用字元陣列的方式處理字串,而不需要使用動態記憶體配置。無法跨越平台,是因為 C 語言適合用來打造底層的嵌入式系統,可以直接連結組合語言協同工作。從這個角度看來,C 語言的設計其實是相當精巧的,這也是 C 語言為何經歷四十年而不衰的原因。

學習 C 語言的好處

C 語言幾乎是當今被廣泛使用的語言當中,唯一同時具有高階與低階特性的語言,這個特性主要是由指標所造成的。利用指標,您可以用記憶體映射的方法存取記憶體,這讓 C 語言可以直接與周邊裝置溝通,因此許多裝置驅動程式可以用 C 語言撰寫,而不需要全部用組合語言。

學習 C 語言的投資報酬率,必須以數十年甚至一輩子的眼光來看,而不是短視的。許多其他的語言,多如過江之鯽,每個兩三年就必須學習全新的語言,就像流行音樂或服飾一般,學會之後很快就會膩了。C 語言絕對不是流行的語言,而是一種經典的、長久的、耐用的語言,您在 C 語言的投資不會浪費,因為 C 語言將會陪伴您,走過數十年,甚至是一輩子。

語言的歷史

  • 1972 -- 貝爾實驗室的 Dennis Richie 在 B 語言的基礎上發展出 C 語言。
  • 1973 -- C 語言加入 struct 之後,UNIX 的上層整個以 C 語言改寫,這個 UNIX+C 的組合極為成功,開啟了《作業系統和程式語言》的新頁。
    • 原本我以為這版的 UNIX 是第一個用高階語言寫的作業系統,但是根據 Jserv 指出,從 UNIX v1 開始,工具程式就不全然用組合語言開發, B 語言解析器已經在 v2 使用。而用高階語言撰寫的作業系統更是不少,像是 Multics 就用 PL/I 這個遠比 C 語言高階、出現年代更早的程式語言開發。
  • 1975 -- UNIX 第六版公布,C 語言開始引起人們的注意。
  • 1978 -- UNIX 第七版相當成功,該版本的 C 成為標準的 C 語言。
  • 1983 -- ANSI 協會制定了 ANSI C 的標準。
  • 1990 -- ISO 組織制訂了 ISO C 的標準。