sysprog
主講人: jserv / 課程討論區: 2017 年系統軟體課程
像英文一樣,C 是種進化的語言。語言的變化可能是好的,字彙的擴充可容納新的觀念與需求。但若不能加以約束,而任由使用者恣意變更,則可能導致問題叢生,引發不能相容的「方言」。從 1987 年開始,C 標準的定義就一直沿用《The C Programming Language》(Kernighan & Ritchie) 書本的 reference 一節。C 語言最初使用於 UNIX 作業系統環境,之後則廣泛使用於 Apple DOS, MS-DOS 等環境中,軟體開發商對於跨平台的需求越來越高,於是 C 語言標準制定因應而生。
Jonathan Adamczewski 貼出經典著作《The C Programming Language》,然後評註說:
"C++: The Good Parts"
早期的 C 語言
最早的 C 語言編譯器由 Dennis Ritchie 開發,使用 recursive descent parser, 並輸出 PDP-11 機械碼。而之後許多編譯器引入 yacc 來產生 parser,也因此,yacc 稱為 compiler-compiler。
Recursive descent parser 為什麼是這樣的名稱呢?
看過上圖,就不難理解,採用遞迴去實做的語法解析器,稱為 Recursive descent parser。Recursive descent parser 很好實作 (就是程式很好寫),難的是什麼?如何將語法規則寫出來,並且還要需要符合 LL(1)。LL 分析器是自頂向下用的一個分析方法,所以 Recursive descent parser 可用,但文法規則要符合 LL(1)。因此,倘若你的文法規則不符合 LL(1),就需要改寫規則,使其符合 LL(1)。
那什麼是 LL(1)?就是在解析文法時,要多讀一個 token 來判斷,多讀兩個來判度就是 LL(2),以此類推。
1983 年起,ANSI (美國國家標準協會) 著手制定 C 語言標準,於是有了 C89,這在 1989 年定案。相較於 K&R C 的變革:
(void) printf("Black Hole\n");
C 標準函式庫
K&R C 原本的參考定義中,並不包含任何函式庫,甚至連輸入、輸出函式都由編譯器的設計者自行斟酌處理。不過在 K&R 的書中,的確討論到幾個為 UNIX 環境發展的「標準」,其中有些函式像是 open() 和 write(),直接併入 UNIX 作業系統,稱為系統呼叫 (system call),而像是 printf(), fopen(), getchar() 則為了方便使用而開發,隨後納入 UNIX C Library。值得注意的是,getchar() 實際上是定義為 macro,但系統也提供可呼叫的函式。
一般來說,編譯器設計者可建立自己的函式庫,但大多力求接近 UNIX 模型,再來補充個別系統的特色。比方說,IBM DOS 環境需要用到中斷處理 (interrupt),於是編譯器供應商則提供足以開發中斷處理常式 (ISR) 的函式。
過去開發者抱怨 C 執行浮點數運算不快,這是因為浮點數運算的自動提昇 (auto-promotion)。在 ANSI C 標準中,只要接受運算結果的對象和運算元類型相同,就可省略轉換為 double 的過程。
C++
C++ 由 Bell Labs 的 Bjarne Stroustrup 發展,於 1983 年問世,恰好就在 C 語言標準著手制定之際。C++ 的一些特徵,像是 const 關鍵字和 function prototype,被 ANSI C 所採納。從 C 語言演化到 C++ 之前,後者稱為 "C with classes"