本節書摘來自華章計算機《c語言程式設計魔法書:基于c11标準》一書中的第1章,第1.1節,作者: 陳轶 更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視。
本章内容主要對c程式設計語言(以下簡稱c語言)進行大體介紹,包括它的曆史以及c語言标準的演化程序。然後介紹一下c語言程式設計思想,目前主流c語言編譯器以及gnu文法擴充。最後簡單介紹一下從用c語言編寫程式到編譯、建構一個可執行程式的大緻過程。
計算機程式設計語言從對計算機硬體底層的抽象程度進行分類,可分為:機器語言、彙編語言以及進階語言。下面由底層到高層分别介紹這幾種類别的程式設計語言。
1)機器語言是直接通過十六進制數表示目前處理器架構的機器指令碼。指令碼包含了目前指令的功能(比如算術邏輯運算、移位、分支、中斷、i/o等)、寄存器、立即數等多種元素。每種處理器架構所對應的機器碼的位元組長度也各不相同,有些是固定長度的(比如arm、mips等架構),有些是可變長度的(比如x86架構)。
2)彙編語言(assembly language)通過簡單的指令助記符(memonics)來表示對應機器指令的功能、寄存器編号、立即數(immediates)等元素。彙編語言是對機器指令的簡單抽象,通過彙編器(assembler)可以将彙編語句翻譯成對應的機器指令碼。
3)進階語言的表達形式更為抽象且貼近我們日常的語言表述。而且,進階語言比起彙編語言往往更具有表達力,且擁有更加豐富的文法特性,以便将程式進行結構化和子產品化。比如,進階語言具有自定義變量辨別符、自定義資料結構、分支與循環、更形象自然的表達式等。進階語言一般通過編譯器(compiler)可直接将表達式翻譯為對應的機器指令碼;也可以将進階語言先翻譯為中間語言(類似于彙編,但可能比彙編适用範圍更廣、更利于跨平台的位元組碼),最後将中間語言翻譯為最終的機器指令碼。
當然,有些書中還介紹了第四代語言,它基于進階語言,比進階語言更抽象,隻需要一些簡單的描述語句就能讓計算機做比較複雜的工作。比如sql(結構化查詢語言,用于資料庫查詢)算是一種第四代語言。
下面,為了能讓大家對這三種層次的程式設計語言有一個感性的認識,這裡将列舉armv8架構處理器下的機器語言、彙編語言,加上它們相應的c語言。讀者如果手頭有xcode,并且有包含apple a7或更高版本處理器的ios裝置的話,可以直接編譯運作,并能看到最終效果。
下面首先列出一個檔案名為my_sub.s的彙編源檔案,其中包含了機器語言和彙編語言。見代碼清單1-1:
在代碼清單1-1中,_my_sub_machine程式片段中的兩條.long語句即為機器指令。這兩條機器指令正好與_my_sub_assembly中的兩條彙編指令相對應。也就是說,“0x4b010000”這串32位的十六進制代碼意思就是“sub w0, w0, w1”,表示将寄存器w0與寄存器w1的值進行相減,然後将結果寫回w0寄存器中。而“0xd65f03c0”指令碼對應于“ret”(更确切地說是ret x30),表示傳回目前過程(procedure)。在彙編語言中,一般會使用過程或者例程(routine)來表示一個可執行的程式片段。在c語言中一般都用函數(function)表示。我們在這裡能夠明顯看到,彙編語言采用指令助記符的方式比寫機器指令碼要直覺得多,而且也不容易出錯。“sub”指令的功能從助記符上就能知道是“減法”功能;而w0、w1也明确指明了使用的寄存器是w0和w1。這些在“0x4b010000”這種機器指令碼上都無法直覺地表現出來。
代碼清單1-2列出c語言是如何表達一個減法操作的。
代碼清單1-2所列出的c語言代碼與代碼清單1-1中的機器指令碼和彙編語言完全對應,意思一目了然——将參數變量a的值與參數變量b的值進行相減,然後将結果傳回。從這裡我們就能看到機器語言、彙編語言以及以c語言為代表的進階語言之間在表達力上的差距了。進階語言的目的就是為了給程式員提供更良好的程式設計工具,更簡潔、更富有表達力的語言,使得我們程式員能提升生産力,并且能構思出更多精彩炫酷的應用,而不是把太多的精力都投入在如何讓計算機執行的細節上。
代碼清單1-3能讓我們在主函數或其他函數中測試上述已經編寫好的函數。
執行了上述代碼之後,我們最後能在控制台看到輸出結果:“three results: 8, 2, 4”。可見,上述三種不同的程式設計語言,計算功能是完全一緻的,都是對兩個輸入參數做減法操作,然後傳回內插補點。然而就可讀性、可了解性以及程式設計便利性而言,顯然c語言比起其他兩者要強得多。而可讀性最差的無疑就是機器指令碼了。
1.c語言的類别與産生
對于進階語言來說,從表達上又可分為指令式程式設計語言(imperative programming language)和陳述型程式設計語言(declarative programming language)。指令式語言主要包括過程式(procedural)、結構化(structured)以及面向對象(object-oriented)的程式設計語言;陳述型程式設計語言主要包括函數式(functional)以及邏輯型(logical)程式設計語言。而c語言則屬于結構化的指令式程式設計語言。不過現在很多指令式程式設計語言也包含了一些函數式程式設計語言的特征。在本書中,後面第18章中談到的blocks文法就是一個很典型的函數式程式設計語言的文法。
c語言最初由dennis ritchie于1969年到1973年在at&t貝爾實驗室裡開發出來,主要用于重新實作unix作業系統。此時,c語言又被稱為k&r c。其中,k表示kernighan的首字母,而r則是ritchie的首字母。k&r c語言與後來标準化的c語言有很大差異。比如,如果函數傳回類型為int,則int可省:int my_function() { },也可以寫成my_function(){ }。編譯器不會有任何警告,更不會報錯。另外,還有現在看來比較奇葩的函數定義,像我們現在定義這麼一個函數——void my_function(int a, char p){ },如果是用k&r c文法定義的話要寫成:void my_function(a, p) int a; char p; { }。k&r的c文法中,定義一個函數時,其形參清單先列出形參的辨別符,然後在函數聲明的後面緊跟着對形參辨別符的完整聲明,最後是函數體。這在現行标準中已經被逐漸廢棄使用了。另外,當時的第一本c語言專業書《the c programming language》也并非一個正式的程式設計語言規範,但被用了許多年。
2.c90标準
由于c語言被各大公司所使用(包括當時處于鼎盛時期的ibm pc),是以到了1989年,c語言由美國國家标準協會(ansi)進行了标準化,此時c語言又被稱為ansi c。而僅過一年,ansi c就被國際标準化組織iso給采納了。此時,c語言在iso中有了一個官方名稱——iso/iec 9899:1990。其中,9899是c語言在iso标準中的代号,像c++在iso标準中的代号是14882。而冒号後面的1990表示目前修訂好的版本是在1990年釋出的。對于iso/iec 9899:1990的俗稱或簡稱,有些地方稱為c89,有些地方稱為c90,或者c89/90。不管怎麼稱呼,它們都指代這個最初的c語言國際标準。這個版本的c語言标準作為k&r c的一個超集(即k&r c是此标準c的一個子集),把後來引入的許多非官方特性也一起整合了進去。其中包括了從c++借鑒的函數原型(function prototypes),指向void的指針,對國際字元集以及本地語言環境的支援。在此标準中,盡管已經将函數定義的方式改為現在我們常用的那種方式,不過k&r的文法形式仍然相容。
3.c99标準
在随後的幾年裡,c語言的标準化委員會又不斷地對c語言進行改進,到了1999年,正式釋出了iso/iec 9899:1999,簡稱為c99标準。c99标準引入了許多特性,包括内聯函數(inline functions)、可變長度的數組、靈活的數組成員(用于結構體)、複合字面量、指定成員的初始化器、對ieee754浮點數的改進、支援不定參數個數的宏定義,在資料類型上還增加了long long int以及複數類型。毫不誇張地說,即便到目前為止,很少有c語言編譯器是完整支援c99的。像主流的gcc以及clang編譯器都能支援高達90%以上,而微軟的visual studio 2015中的c編譯器隻能支援到70%左右。
4.c11标準
2007年,c語言标準委員會又重新開始修訂c語言,到了2011年正式釋出了iso/iec 9899:2011,簡稱為c11标準。c11标準新引入的特征盡管沒c99相對c90引入的那麼多,但是這些也都十分有用,比如:位元組對齊說明符、泛型機制(generic selection)、對多線程的支援、靜态斷言、原子操作以及對unicode的支援。本書将主要針對c11标準為大家詳細講解c程式設計語言
筆者近兩年也是在不斷地了解c語言标準委員會的最新動态,其中看到有人提出想為c語言添加面向對象的特性,包括增加類、繼承、多态等已被c++語言所廣泛使用的文法特性,但是最終被委員會駁回了。因為這些複雜的文法特性并不符合c語言的設計理念以及設計哲學,況且c++已經有了這些特性,c語言無需再對它們進行支援。筆者将在第19章給大家談談c語言設計理念與發展方向。