《編程語言的自舉之路——從機(jī)器碼到高級(jí)語言》源站鏈接,閱讀體驗(yàn)更佳
語言是協(xié)議
在中國(guó),大多數(shù)人使用漢語進(jìn)行交流,我對(duì)你說一句漢語你能聽懂并做出回應(yīng);如果有人對(duì)你說一句英語,而你又正好學(xué)習(xí)了英語,那么你也能聽懂并做出回應(yīng)。
可見我們平常所使用的自然語言是人與人之間交流的一種協(xié)議,不同的語言就是不同的協(xié)議,但是因?yàn)椴煌恼Z言都有一個(gè)統(tǒng)一的物理基礎(chǔ)——地球,所有文明的誕生環(huán)境都是地球,所有語言的目的也都是一樣的,那就是描述地球,記錄一些物或者事,所以不同的自然語言之間可以相互翻譯。而如果我們掌握了多門自然語言也就相當(dāng)于掌握了多種描述這個(gè)世界的手段。
高級(jí)程序設(shè)計(jì)語言和自然語言有著相似之處:
高級(jí)程序設(shè)計(jì)語言是程序員之間的協(xié)議,但是其首先是人與計(jì)算機(jī)之間的協(xié)議,那高級(jí)程序設(shè)計(jì)語言是怎么指揮計(jì)算機(jī)干活的呢?
高級(jí)程序設(shè)計(jì)語言的誕生
眾所周知,計(jì)算機(jī)只認(rèn)識(shí)0101這樣的機(jī)器碼,當(dāng)然人類也可以讀懂機(jī)器碼,因?yàn)檎麄€(gè)計(jì)算機(jī)世界都是人類創(chuàng)造的,但是人類甚至是制定機(jī)器碼的人想要看懂機(jī)器碼都得花費(fèi)老大的力氣。
這可不行,人類發(fā)明計(jì)算機(jī)是為人類服務(wù)的,是為了偷懶的,不能反過來把自己給累著,得想辦法讓機(jī)器碼更容易閱讀。
計(jì)算機(jī)所認(rèn)識(shí)的機(jī)器碼指令并不是無規(guī)律的0101,一條計(jì)算機(jī)指令一般分為代表動(dòng)作的部分和代表操作目標(biāo)的部分。
經(jīng)過上面的處理,我們就成功用易于人類閱讀的自然語言助記符對(duì)0101序列進(jìn)行了替代,而且它也很容易轉(zhuǎn)換成0101序列供計(jì)算機(jī)使用。
以后,我們?cè)倬帉懘a就不用直接使用0101了,只需要使用我們上面所定義的助記符來編寫代碼,然后再對(duì)照著助記符所對(duì)應(yīng)的實(shí)際機(jī)器碼把助記符代碼轉(zhuǎn)換成二進(jìn)制機(jī)器碼就可以運(yùn)行了。
上面所說的助記符其實(shí)就是最簡(jiǎn)單的匯編助記符語言,而那些代表計(jì)算機(jī)動(dòng)作的助記符的集合就構(gòu)成了一個(gè)CPU平臺(tái)的指令集。在某個(gè)CPU上運(yùn)行的程序的所有功能都是依靠這個(gè)CPU平臺(tái)所提供的指令集實(shí)現(xiàn)的,包括高級(jí)程序設(shè)計(jì)語言。
匯編語言的出現(xiàn),為應(yīng)用程序開發(fā)者提供了第一道屏蔽底層硬件的屏障,因?yàn)椴煌珻PU平臺(tái)的指令集和寄存器名所對(duì)應(yīng)的0101序列可能是不一樣的,但是不同的0101序列如果具有的功能是一樣的,那我們就可以把不同的機(jī)器碼映射到一套相同的助記符上面,這在很大程度上屏蔽了不同硬件平臺(tái)的不同。
后來,匯編中又加入了一些宏指令、編譯制導(dǎo)指令和偽指令,也具有了一定的可用性。但是匯編代碼還是太靠近機(jī)器了。
比如,我們要寫一個(gè)運(yùn)算1+1并且把運(yùn)算結(jié)果輸出到屏幕上的程序,我們要事無巨細(xì)的把所有細(xì)節(jié)都告訴計(jì)算機(jī):我們要告訴計(jì)算機(jī)把一個(gè)8位的數(shù)字1加載到AH,把一個(gè)8位的數(shù)字1加載到AL,把AL和AH中的數(shù)據(jù)相加并把結(jié)果保存到AH中,最后我們還要把AH中的計(jì)算結(jié)果Store到顯存中以在屏幕上輸出(上面的例子基于8086平臺(tái)的指令集和寄存器)。
這要求軟件開發(fā)人員對(duì)硬件資源有相當(dāng)程度的理解,而且代碼的信噪比簡(jiǎn)直低到令人發(fā)指——我只想寫個(gè)1+1,還要自己組織內(nèi)存和寄存器,與邏輯無關(guān)的信息太多了。
使用匯編操作內(nèi)存也是非常不舒服的,比如在內(nèi)存地址為0001的長(zhǎng)度為2字節(jié)的存儲(chǔ)區(qū)域上存了一個(gè)數(shù)字,而我在取的時(shí)候不小心寫錯(cuò)了,那整個(gè)程序的邏輯就會(huì)出現(xiàn)錯(cuò)誤。當(dāng)然匯編中也不是只能使用內(nèi)存地址來進(jìn)行內(nèi)存訪問,也可以使用標(biāo)識(shí)符;但是匯編中在聲明一個(gè)標(biāo)識(shí)符的時(shí)候只能聲明這個(gè)標(biāo)識(shí)符的步長(zhǎng),也就是想要申請(qǐng)的內(nèi)存單元的個(gè)數(shù),標(biāo)識(shí)符本身代表的是所申請(qǐng)的存儲(chǔ)區(qū)域的第一個(gè)內(nèi)存單元的地址,我們?cè)诤罄m(xù)的使用中可能還是需要通過偏移量來進(jìn)行訪存;而且在進(jìn)行內(nèi)存訪問的時(shí)候?qū)ぶ贩绞揭彩嵌喾N多樣的,這就讓程序的可讀性變得很低。
還存在一個(gè)很大的問題,對(duì)于存儲(chǔ)在某個(gè)標(biāo)識(shí)符所代表的內(nèi)存空間中的數(shù)據(jù),它是數(shù)字呢還是一個(gè)字符串呢還是一個(gè)地址信息呢?這些都是需要開發(fā)人員存在自己的大腦中的。而關(guān)鍵的問題是不管這些數(shù)據(jù)實(shí)際上具有什么樣的作用,它在內(nèi)存中的表現(xiàn)形式都是一堆0101,沒有任何的不同,也就是說我們可以任意的理解這些數(shù)據(jù),任意的對(duì)它們進(jìn)行操作,這約束太少了,太過危險(xiǎn)了。同時(shí),因?yàn)槠湎拗坪苌伲绦騿T出錯(cuò)的概率也是居高不下。
高級(jí)程序設(shè)計(jì)語言
能不能讓代碼更貼近人類一些呢?高級(jí)程序設(shè)計(jì)語言應(yīng)運(yùn)而生。
高級(jí)程序設(shè)計(jì)語言更貼近人類,更易于人類閱讀,非常接近于人們習(xí)慣使用的自然語言和數(shù)學(xué)語言,離機(jī)器更遠(yuǎn),離人類更近,故稱其為“高級(jí)語言”。比如我們要用C語言寫上面所說的1+1的示例程序,代碼為:("%d", 1+1);。
怎么樣,是不是有點(diǎn)見碼知意的感覺了呢?
更重要的是,高級(jí)語言幾乎把我們從硬件中解放了出來,高級(jí)程序設(shè)計(jì)語言為我們屏蔽了直接操作硬件資源的代碼,在匯編中代表動(dòng)作的指令全部都隱藏在了高級(jí)程序設(shè)計(jì)語言的上下文中,比如下面這段C語言代碼:
int a = 1, b = 2;

int c = a + b;
printf("1 + 2 is %d", c);
上面短短的幾行代碼在轉(zhuǎn)換為匯編的時(shí)候會(huì)有申請(qǐng)對(duì)應(yīng)大小內(nèi)存的操作、將數(shù)據(jù)加載到寄存器中的操作、對(duì)寄存器中的數(shù)據(jù)進(jìn)行加法運(yùn)算的操作,最后還有把寄存器中的數(shù)據(jù)寫到顯存的操作。
但是,事情還沒有結(jié)束,計(jì)算機(jī)能運(yùn)行的只有0101這樣的機(jī)器碼,我們?cè)诟呒?jí)語言中連匯編的影子都很難看到,更別提0101這樣的機(jī)器碼了,但是高級(jí)語言想要在計(jì)算機(jī)上運(yùn)行,我們勢(shì)必要把高級(jí)語言的代碼轉(zhuǎn)換為0101這樣的機(jī)器碼。
匯編助記符和機(jī)器碼之間是一一對(duì)應(yīng)的關(guān)系,我們可以認(rèn)為匯編代碼只是機(jī)器碼的助記符,所以匯編語言可以很容易轉(zhuǎn)換成機(jī)器碼,而把匯編代碼轉(zhuǎn)換成機(jī)器碼的程序被稱為匯編器。
把高級(jí)語言的代碼轉(zhuǎn)換為機(jī)器碼就顯得比較復(fù)雜了,因?yàn)樗幌駞R編語言那樣和機(jī)器碼之間存在著一一對(duì)應(yīng)的關(guān)系,而且高級(jí)語言的運(yùn)行方式也并不是唯一的,以C語言這種編譯運(yùn)行的語言為例匯編語言轉(zhuǎn)換成機(jī)器碼,從編碼結(jié)束到順利運(yùn)行需要經(jīng)歷編譯->鏈接->運(yùn)行這樣的生命周期,而編譯又分為詞法分析、語法分析、語義分析等步驟,鏈接又分為靜態(tài)鏈接和動(dòng)態(tài)鏈接等手段。我們后面會(huì)有專門的文章來介紹高級(jí)語言是如何運(yùn)行起來的,這里就不做過多贅述了。但是無論這個(gè)過程多么復(fù)雜,其本質(zhì)上都是一種映射,這也就意味著高級(jí)語言和機(jī)器碼之前其實(shí)存在著潛在的一對(duì)一的關(guān)系。
匯編語言和機(jī)器碼的一一對(duì)應(yīng)關(guān)系是明面上的,我們甚至可以認(rèn)為有一張兩列的表格,第一列是一條匯編指令,而第二列就是其對(duì)應(yīng)的機(jī)器碼。但是高級(jí)語言和機(jī)器碼之間的一一對(duì)應(yīng)關(guān)系是如何保證的呢?答案是由規(guī)則來保證的,一門高級(jí)語言一定會(huì)有自己的關(guān)鍵字,有規(guī)定的語法等等規(guī)則,我們?cè)谑褂酶呒?jí)語言編寫代碼的時(shí)候,必須使用高級(jí)語言規(guī)定的關(guān)鍵字和語法,寫出來的代碼才能被這門高級(jí)語言的編譯器處理成可以執(zhí)行的機(jī)器碼。
然而,萬變不離其宗,高級(jí)語言在怎么高級(jí),它最終都是要被轉(zhuǎn)換為機(jī)器碼的,這也就意味著,不同的高級(jí)語言肯定具有很多相通的地方,甚至我們可以總結(jié)出一門高級(jí)語言需要具備什么樣的基本要素才能被順利地轉(zhuǎn)換為機(jī)器碼,這就是我們下一篇文章將要介紹的內(nèi)容。
其實(shí),想要把高級(jí)語言代碼直接轉(zhuǎn)換為機(jī)器碼是比較費(fèi)力不討好的,因?yàn)椴煌挠布脚_(tái)的差異可能是比較大的,而翻譯高級(jí)語言的工作又是比較復(fù)雜的匯編語言轉(zhuǎn)換成機(jī)器碼,如果我們直接把高級(jí)語言翻譯成機(jī)器碼,那么我們可能就需要在不同的硬件平臺(tái)上都費(fèi)時(shí)費(fèi)力的做這項(xiàng)工作,這可不就是費(fèi)力不討好么。
前面我們?cè)?jīng)提到過,匯編語言為軟件開發(fā)人員提供了第一道屏蔽硬件平臺(tái)差異的屏障,因?yàn)椴煌挠布脚_(tái)的機(jī)器碼可能是不一樣的,但是我們可以把不同的機(jī)器碼映射到一套相同的匯編助記符上,這樣,在后續(xù)硬件平臺(tái)升級(jí)的時(shí)候,雖然機(jī)器碼在一直變化,但是它可以運(yùn)行的匯編指令的變化就會(huì)小很多,也就是說硬件平臺(tái)的指令集相對(duì)變得穩(wěn)定下來,如此一來,我們何不先把高級(jí)語言的代碼轉(zhuǎn)換成匯編代碼,然后再由匯編器進(jìn)行下一步的動(dòng)作呢。實(shí)際上,大部分編譯運(yùn)行的語言的編譯器都是這么做的。
關(guān)于更多的細(xì)節(jié),我會(huì)有專門介紹編譯原理的文章,到時(shí)候歡迎惠讀。
為什么高級(jí)程序設(shè)計(jì)語言如此之多
上文只是流水賬式的說明了一下高級(jí)程序設(shè)計(jì)語言的誕生過程,隨著計(jì)算機(jī)軟件的不斷發(fā)展,各種各樣的高級(jí)程序設(shè)計(jì)語言層出不窮,到目前為止,編程語言應(yīng)該有幾百種了。
世界上有多種自然語言還是可以理解的,因?yàn)椴煌奈拿麟m然都是誕生于地球,但是誕生它們的文明是不同的,比如中文誕生于農(nóng)耕文明,英文誕生于航海文明,人們面臨的生存環(huán)境不同,不同的自然語言之間有所差異也是理所當(dāng)然的。
類似的,雖然所有的高級(jí)程序設(shè)計(jì)語言的誕生都是基于相同的計(jì)算機(jī)體系結(jié)構(gòu),但是它們想要解決的問題領(lǐng)域卻是不同的。
這就好比大廚的工具箱中的刀具有很多種一樣,不同的刀具適合處理不同的食材,編程語言也是一樣的,不同的語言有不同的特性,有不同的生態(tài),適合應(yīng)用于不同的問題領(lǐng)域,在某個(gè)領(lǐng)域使用某種語言能達(dá)到產(chǎn)投比的最大化。比如在嵌入式領(lǐng)域,匯編和C是首選;在操作系統(tǒng)領(lǐng)域,C是首選;在系統(tǒng)級(jí)服務(wù)編程領(lǐng)域,C++是首選;在企業(yè)級(jí)應(yīng)用程序和Web應(yīng)用領(lǐng)域,Java是首選。
那么我們有必要學(xué)習(xí)和掌握多門編程語言嗎?說的現(xiàn)實(shí)一點(diǎn),我們所做的任何努力都是有所求的,也就是說我們?cè)趯W(xué)習(xí)編程語言的時(shí)候要根據(jù)自己的工作領(lǐng)域,以提高自己的生產(chǎn)力和行業(yè)價(jià)值為主要目的去學(xué)習(xí),這就是傳說中的面向人民幣編程。當(dāng)然,如果是興趣使然,則可以多涉獵一些,但是在學(xué)習(xí)的過程中一定要有側(cè)重,只有選好自己的方向和領(lǐng)域,明確哪些技術(shù)能給自己帶來收益,而又有哪些技術(shù)是用以擴(kuò)展視野和提升思維的,才能不“亂碼漸欲迷人眼”。
本人為自己制定的計(jì)劃就是,主攻領(lǐng)域,同時(shí)學(xué)習(xí)其他的編程語言來擴(kuò)展自己的視野,提升自己的思維境界。在我后續(xù)的文章中會(huì)有關(guān)于多門編程語言基本特性介紹的文章。
雞生蛋還是蛋生雞?軟件世界一直在自舉
把匯編語言轉(zhuǎn)換成機(jī)器碼的動(dòng)作是匯編器幫我們完成的,把高級(jí)語言轉(zhuǎn)換成機(jī)器碼的動(dòng)作是編譯器幫我們完成的。而匯編器和編譯器都是一種軟件,但是它們最初是怎么被開發(fā)出來的呢?
毫無疑問,世界上第一個(gè)匯編器肯定不是用匯編語言編寫的,同樣的,世界上第一個(gè)C語言的編譯器也肯定不是用C語言開發(fā)的,這是一個(gè)典型的雞或蛋的問題。
世界上第一個(gè)匯編器肯定需要使用機(jī)器碼進(jìn)行編寫,等到這個(gè)用機(jī)器碼開發(fā)的匯編器可以把現(xiàn)有的所有的匯編語言的指令集完整無誤得轉(zhuǎn)換為機(jī)器碼的時(shí)候,它的使命就可以結(jié)束了,因?yàn)閷?duì)后續(xù)匯編器的升級(jí),我們完全可以基于現(xiàn)有的匯編語言編寫出新版本的匯編器,然后用現(xiàn)有的匯編器匯編新版的匯編器,這樣,在以后匯編語言的升級(jí)迭代中就不需要機(jī)器語言的參與了。C語言的過程也是類似的,而現(xiàn)在新版本的C語言的編譯器也都是用C語言來進(jìn)行編寫的。
**當(dāng)一門語言可以用自己實(shí)現(xiàn)自己的編譯器的時(shí)候,我們就說一門語言實(shí)現(xiàn)了自舉,**更多關(guān)于自舉的內(nèi)容大家可以參考維基百科。
不止是在編程語言的發(fā)展上,其實(shí),“自舉”這種現(xiàn)象在軟件世界中是非常常見的,比如現(xiàn)在我們的應(yīng)用軟件的運(yùn)行環(huán)境一般也都不是裸機(jī)了,而是運(yùn)行在操作系統(tǒng)之上的,從大的方面來說,軟件世界其實(shí)一直是在自舉的向前發(fā)展的。
感謝你耐心讀完。本人深知技術(shù)水平和表達(dá)能力有限,如果文中有什么地方不合理或者你有其他不同的思考和看法,歡迎隨時(shí)和我進(jìn)行討論()。