1.機器語言>彙編語言>進階語言
語言是人與人的一種交流工具,就比如我現在用漢語來寫這篇博文來交流探讨技術問題;程式設計語言也是如此,隻是交流對象不是人而是機器。我可以用漢語來寫博文,也可以用英語來寫(假如我英語熟練);我可以用PHP來寫一個網站,也可以用ASP.NET來寫。這就說明語言的本質就是一種交流工具,而我選擇哪種語言來交流并不會影響我要的結果。然而在實際中到底要選用那個語言确要根據具體情況而定,這是個成本問題,比如我如果今天腦子抽筋要用日語,那我寫着也累(關鍵是也不會)、讀者或許也會罵娘了。
早期的計算機是一台超級龐然大物,能耗抵得上一家小型工廠,而計算能力确不如今天的一台手持電腦。也就是當時人力成本要遠遠低于這台機器的機器成本,也就決定了當時是以機器為中心,而不是人。是以人們用計算機能直接了解的機器語言來編寫程式。這裡有一個計算兩個整數的最大公約數的栗子(MIPS R4000處理器的機器語言),16進制形式如下:
1 2dbdffd0 afbf0014 oc1002a8 00000000 0c1002a8 afa2001c 8fa4001c
2 00401825 10820008 0064082a 10200003 00000000 10000002 00832023
3 00641823 1483fffa 0064082a 0c1002b2 00000000 8fbf0014 27bd0020
4 03e00008 00001025
這些機器語言對人的閱讀和了解相當的不友好。當程式越來越複雜,人們迫切需要一種簡單不易出錯的記法形式,于是人們創造了彙編語言,用簡短的詞語來助記機器操作。用MIPS R4000處理器的彙編語言重寫上面的栗子如下:
1 addiu sp,sp-32
2 sw ra,20(sp)
3 jal getint
4 nop
5 jal getint
6 sw v0,28(sp)
7 lw a0,28(sp)
8 move v1,v0
9 beq a0,v0,D
10 slt at,v1,a0
11 A: beq at,zero,B
12 nop
13 b C
14 subu a0,a0,v1
15 B: subu v1,v1,a0
16 C: bne a0,v1,A
17 slt at,v1,a0
18 D: jal putint
19 nop
20 lw ra,20(sp)
21 addiu sp,sp,32
22 jr ra
23 move v0,zer
對于這個栗子,閱讀和了解上明顯要比機器語言要容易得多。然而這種編碼形式計算機是不識别的,需要一種叫做“彙編器”的程式來翻譯成對應的機器的機器語言才能執行。這種一個彙編指令對應一個機器之路的關系還是顯而易見的,對于不同的計算機,則還需不同的彙編語言來編寫;這個時代的程式設計仍然是以機器為中心,程式員也許要以機器的思維來解決問題。
就如同機器語言到彙編語言的過渡一樣,程式越來越複雜、各種類型的計算機越來越多,為每一種機器都編寫程式也越來越困難、人力成本也越來越大。
根據計算機科學領域慣用的抽象原則,人們需要一個中間層來隔離某一個計算操作對具體機器的具體操作指令的這種對應關系,也就是這個中間層需要獨立于具體的機器操作。随後人們就創造出了進階程式設計語言來做這麼一個中間層,由于剝離了計算操作到具體機器操作的對應關系,但是機器具體執行的依然還是機器操作,那麼也就需要一個更“進階的翻譯器”來做這項翻譯工作,這個進階的翻譯器就是現在所說的編譯器。
由于這種翻譯工作是由機器來完成的,在編譯器出現的早期時候,人們總是能夠寫出比進階語言代碼更高效的相應的彙編語言(畢竟早期的時候計算機的計算能力還一種奢侈的資源)。但是随着程式進一步的擴大、硬體性能的提升、編譯器的優化、程式的維護成本等等變化,這種性能差異和人力成本相比慢慢也就顯得微不足道了。現在想一想,當初c++面世的時候、基于虛拟機技術的java出現時也是這麼一種情況。
自從進階程式設計語言出現以後,程式設計慢慢開始從以機器為中心向以人為中心轉變。
2.編譯和解釋
按照抽象的觀點來看,進階語言的編譯和執行大緻是如下的樣子:

編譯器把源程式翻譯成目标程式(典型的是機器語言程式)後就隐退了,後面的程式的運作中就不在需要編譯器的存在了。
解釋器是另外一種實作進階語言的方式:
和編譯器不同的是,解釋器一直全程守候。解釋器本身相當于一個“虛拟機”,它的“機器語言”就是源程式的進階語言。和編譯器相比,解釋器可以有更大的靈活性。它可以輕易地做到在程式運作初期來生成某一塊新的代碼,然後再後續的某一個時間去執行它;以及把一些決策推遲到運作時再去做決定。和解釋器相對應,編譯型的語言通常可以帶來較好的性能,它可以在編譯時期就把一些決策确定下來,比如一些記憶體布局。
雖然兩者的概念很清晰,也有較大的差異性,然而現實中有些語言是“混合型”的,流程大緻如下:
如果初期階段的翻譯器比較簡單,則我們把它稱為解釋型語言;如果翻譯階段複雜,則成為編譯型語言。然而簡單和複雜本身是個形容詞,而不能可量化。完全有可能出現一個複雜的翻譯器負責中間語言程式的生成,一個複雜的虛拟機(解釋器)來執行中間語言,JAVA、.NET也正是這種實作方式。
2.1連接配接器和預處理器
我們所用的進階語言所寫的程式通常還需要一個庫(包含一些IO、網絡等等的操作),在編譯的時候就需要把我們寫的源程式和庫程式連結到一起來生成目标代碼,比如我要讀寫檔案,則需要連結IO相關的庫程式。通常情況下目标代碼為彙編語言而不是機器語言,這種方式更有利于調試程式,也便于閱讀,更能把編譯器和機器語言檔案格式的變化隔離開來。預處理器可以提供一種條件編譯的功能,比如C#中編譯條件#if DEBUG這些東西,再如C、C++中的宏機制也是屬于預編譯的範濤。
3.程式語言的設計實作以及分類
現實中語言的設計和實作總是有不可調和的沖突,從設計角度來看總是希望一個語言能有更強大的表達能力,然而卻總是受到語言的實作制約。這根本原因大概也許是以人為中心和以機器為中心的沖突吧,因為你的語言的執行最終還是離不開機器的執行,是以語言設計者也是不得不在這兩者中間尋求平衡。
依據現有的計算模型(運算産生結果或運算影響結果),大緻分為兩大類(當然這些所謂的分類也是從不同角度得來的,如果你站在另外的角度看,或許就是另外一番景象了)說明式語言和指令式語言。從某種的角度看,說明式語言明顯要更“進階”,因為它更貼近使用者-程式員,而更遠離實作者-語言設計者。然而現在的語言還是指令式占據着統治地位,主要是因為實作的難度、性能的要求等原因制約着說明式語言的發展。上層是抽象的進階語言,下層是實作的細節,抽象層級越高,實作的難度則越大,因為你要隔離隐藏的細節越複雜。
函數式語言是說明式語言中的一個,近年它的熱度也是急劇上升來着;我們熟知的C、C++、JAVA,C#等都是屬于馮諾依曼體系的範濤,當然也是指令式語言的子集。和函數式語言中的把函數和數值作為語言的一等公民(可以指派為給變量、作為參數傳遞、作為傳回值處理)不一樣的是,馮諾依曼語言的中的基本操作是指派語句,它們通過“副作用”去影響後續的計算結果。
腳本語言比如JS,它們一般都是擁有強大靈活度的解釋性語言,一般充當着“粘結劑”的角色。等等還有諸多的語言分類,就不在此贅述了。
總結
本篇介紹了語言的從機器語言到彙編再到進階語言的演進過程以及其發展的驅動力;以及進階語言的兩種實作方式“解釋”和“編譯”的差異;以及按照語言的計算模型進行的語言分類。因為這些都是我個人了解的一家之言,難免會有一些錯誤或者不嚴謹的地方,歡迎園友們不吝賜教。
作者:
Blackheart出處:
http://linianhui.cnblogs.com本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。