# C 語言演化背景和編譯器原理 ###### tags: `sysprog` :::info 主講人: [jserv](http://wiki.csie.ncku.edu.tw/User/jserv) / 課程討論區: [2017 年系統軟體課程](https://www.facebook.com/groups/system.software2017/) :mega: 返回「[嵌入式作業系統設計與實作](http://wiki.csie.ncku.edu.tw/sysprog/schedule)」課程進度表 :::  * 25 年之間的變化 [ [source](https://twitter.com/historyinmoment/status/719193743245516801/photo/1) ] ## C 語言的演化 像英文一樣,C 是種進化的語言。語言的變化可能是好的,字彙的擴充可容納新的觀念與需求。但若不能加以約束,而任由使用者恣意變更,則可能導致問題叢生,引發不能相容的「方言」。從 1987 年開始,C 標準的定義就一直沿用《[The C Programming Language](https://en.wikipedia.org/wiki/The_C_Programming_Language)》(Kernighan & Ritchie) 書本的 reference 一節。C 語言最初使用於 UNIX 作業系統環境,之後則廣泛使用於 Apple DOS, MS-DOS 等環境中,軟體開發商對於跨平台的需求越來越高,於是 C 語言標準制定因應而生。  * [Jonathan Adamczewski](https://twitter.com/twoscomplement) 貼出經典著作《The C Programming Language》,然後評註說: * "**C++: The Good Parts**" **早期的 C 語言** * [Very early C compilers and language](https://www.bell-labs.com/usr/dmr/www/primevalC.html) * [cc / unix-v1](https://github.com/jserv/unix-v1/tree/master/src/c) * [以工程觀點重新檢視 UNIX 與 C 語言](http://www.slideshare.net/jserv/unix-crevise) * [UNIX Internals](http://www.slideshare.net/jserv/unix-v6study) (Page 54-58) 最早的 C 語言編譯器由 [Dennis Ritchie](https://en.wikipedia.org/wiki/Dennis_Ritchie) 開發,使用 [recursive descent parser](https://en.wikipedia.org/wiki/Recursive_descent_parser), 並輸出 [PDP-11](https://en.wikipedia.org/wiki/PDP-11) 機械碼。而之後許多編譯器引入 yacc 來產生 parser,也因此,yacc 稱為 compiler-compiler。 [Recursive descent parser](https://en.wikipedia.org/wiki/Recursive_descent_parser) 為什麼是這樣的名稱呢?  看過上圖,就不難理解,採用遞迴去實做的語法解析器,稱為 [Recursive descent parser](https://en.wikipedia.org/wiki/Recursive_descent_parser)。[Recursive descent parser](https://en.wikipedia.org/wiki/Recursive_descent_parser) 很好實作 (就是程式很好寫),難的是什麼?如何將語法規則寫出來,並且還要需要符合 LL(1)。LL 分析器是自頂向下用的一個分析方法,所以 [Recursive descent parser](https://en.wikipedia.org/wiki/Recursive_descent_parser) 可用,但文法規則要符合 LL(1)。因此,倘若你的文法規則不符合 LL(1),就需要改寫規則,使其符合 LL(1)。 那什麼是 LL(1)?就是在解析文法時,要多讀一個 token 來判斷,多讀兩個來判度就是 LL(2),以此類推。 1983 年起,ANSI (美國國家標準協會) 著手制定 C 語言標準,於是有了 C89,這在 1989 年定案。相較於 K&R C 的變革: * portability : signed/unsigned, short int * const, volatile, enum, long double * 浮點數支援 * 允許 struct 當作函式參數來傳遞 * void : `(void) printf("Black Hole\n");` * C 標準函式庫,獨立於作業系統 **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" * [ [source](https://twitter.com/RichRogersHDS/status/666798359244611584) ] * In Ruby, everything is an object. * In Clojure, everything is a list. * In Javascript, everything is a terrible mistake. * in C, everything is a representation (unsigned char [sizeof(TYPE)]).  * [ [source](https://isocpp.org/std/status) ] ## 為什麼你該理解編譯器的原理 * 其中一個理由是理解編譯器的限制: [gcc can’t handle too much ](http://peter.kingofcoders.com/?p=1799)[#if](https://embedded2015.hackpad.com/ep/search/?q=%23if&via=DWAUb7NnQF0)[ macro](http://peter.kingofcoders.com/?p=1799) * [Compiler 的多元應用](https://embedded2015.hackpad.com/Compiler-rtfoECtyuOd) * [What Can Compilers Do for Us?](http://www.slideshare.net/jserv/what-can-compilers-do-for-us) * 電腦科學最早的思想體系 ## 編譯器和最佳化原理 * 詳情參閱: [共筆](https://hackmd.io/s/Hy72937Me)
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.