{eval=Array;=+count(Array);}
首先上結(jié)論:
(1)不是所有的編程語(yǔ)言都要先編譯成C
(2)甚至有編程語(yǔ)言連編譯成匯編這一步都跳過(guò)了。
(3)不是所有的編程語(yǔ)言都需要編譯,有的是純解釋型語(yǔ)言
硬件執(zhí)行的本質(zhì)就是一堆電子元件的“開(kāi)關(guān)”動(dòng)作,開(kāi)、關(guān)兩種狀態(tài)可以用二進(jìn)制的1和0來(lái)表示,這樣整個(gè)硬件的執(zhí)行就和二進(jìn)制對(duì)應(yīng)了起來(lái)。硬件是無(wú)法理解上層的高級(jí)編程語(yǔ)言的(比如Java、C++、C,、C#、Javascript…… )。
那么為什么要發(fā)明高級(jí)編程語(yǔ)言呢?因?yàn)椋?/p>
(1)人類直接與二進(jìn)制打交道,比較費(fèi)力,與自然語(yǔ)言相隔太遠(yuǎn),不利于相互交流和協(xié)作,從而很難誕生高效的大規(guī)模團(tuán)隊(duì)協(xié)作來(lái)完成超復(fù)雜規(guī)模的軟件設(shè)計(jì)、開(kāi)發(fā)。
(2)每種硬件平臺(tái)對(duì)應(yīng)的二進(jìn)制設(shè)計(jì)都不太相同,如果直接與二進(jìn)制打交道,那么就要不斷地重復(fù)造輪子,不利于跨平臺(tái)的設(shè)計(jì)和協(xié)作。
既然需要高級(jí)編程語(yǔ)言,而硬件又只認(rèn)識(shí)二進(jìn)制,那么怎么去解決這兩者之間的鴻溝呢?答案就是:編譯和解釋。
你可以把編譯類比成一次性翻譯完整的文學(xué)著作。相當(dāng)于把用高級(jí)編程語(yǔ)言編寫(xiě)的源代碼一次性轉(zhuǎn)換成二進(jìn)制。
人們擺脫二進(jìn)制的第一步嘗試是設(shè)計(jì)匯編語(yǔ)言和開(kāi)發(fā)匯編器。每一條匯編指令與對(duì)應(yīng)的二進(jìn)制是一一對(duì)應(yīng)的,換言之,匯編語(yǔ)言只不過(guò)是二進(jìn)制的一種助記方式。
在有了匯編器之后,可以在匯編語(yǔ)言之上,再來(lái)設(shè)計(jì)高級(jí)編程語(yǔ)言和高級(jí)語(yǔ)言的編譯器,后者可以通過(guò)匯編器轉(zhuǎn)換成二進(jìn)制。通過(guò)這樣的不斷遞歸的設(shè)計(jì)、開(kāi)發(fā)過(guò)程,就可以設(shè)計(jì)出非常高級(jí)的編程語(yǔ)言。具體的原理分析可以參見(jiàn)筆者的專欄《ucloud方舟編譯器源代碼分析》的《先有蛋還是先有雞:編譯器本身是怎么被編譯出來(lái)的?》。
只要有能轉(zhuǎn)換出最終二進(jìn)制的高級(jí)語(yǔ)言編譯器,那么其實(shí)是不需要有顯式的匯編器和匯編語(yǔ)言的。
解釋相對(duì)于同聲傳譯。Javascript這些腳本語(yǔ)言,大部分都是采用解釋器來(lái)做翻譯。它并不需要像編譯那樣,一次性把所有的源代碼轉(zhuǎn)換成最終的二進(jìn)制。而是在運(yùn)行的過(guò)程中,一邊翻譯一邊執(zhí)行。與上面設(shè)計(jì)高級(jí)編程語(yǔ)言的編譯器的方式一樣,高級(jí)語(yǔ)言的計(jì)時(shí)器也可以通過(guò)遞歸的方式設(shè)計(jì)、開(kāi)發(fā)。到最后,只要有能轉(zhuǎn)換出最終二進(jìn)制的高級(jí)語(yǔ)言解釋器,那么其實(shí)是不需要有顯式的匯編器和匯編語(yǔ)言的。
關(guān)于解釋器的具體原理,以及加強(qiáng)型算法——如JIT,可以參見(jiàn)筆者的專欄《ucloud方舟編譯器源代碼分析》的《干掉Java虛擬機(jī)來(lái)提升應(yīng)用程序性能:只有ucloud想到了嗎?》。
歷史上還專門(mén)開(kāi)發(fā)過(guò)面向高級(jí)語(yǔ)言的機(jī)器,中間是不需要匯編語(yǔ)言過(guò)渡的。比如大名鼎鼎的LISP語(yǔ)言。曾經(jīng)人們專門(mén)為L(zhǎng)ISP語(yǔ)言開(kāi)發(fā)過(guò)對(duì)應(yīng)的機(jī)器。但是最終的商業(yè)成果并不理想,主要是當(dāng)時(shí)一些條件還不成熟。
我來(lái)回答這個(gè)問(wèn)題,其實(shí)際情況并不是這樣的,首先要明確的是,我們不管用什么語(yǔ)言編寫(xiě)程序最后都要生成機(jī)器能夠識(shí)別的語(yǔ)言。我以自已最為熟悉的編程語(yǔ)言C語(yǔ)言和匯編語(yǔ)言為例來(lái)說(shuō)明吧!
我們知道計(jì)算機(jī)是不能直接識(shí)別C語(yǔ)言或匯編語(yǔ)言的,要經(jīng)過(guò)編譯軟件把這些人類很容易讀懂的語(yǔ)言生成計(jì)算機(jī)能夠“識(shí)讀”的語(yǔ)言,我們稱之為“機(jī)器語(yǔ)言”。機(jī)器語(yǔ)言都是以“1”和“0”組成的,我們?nèi)祟惡茈y讀懂!
因此,在用編程軟件編寫(xiě)程序時(shí),編程軟件都有把源程序編譯成目標(biāo)文件的功能,比如我們常用的keil軟件,只要通過(guò)簡(jiǎn)單的設(shè)置便會(huì)自動(dòng)生成后綴為.Hex的目標(biāo)文件,我們要把這個(gè)文件通過(guò)下載軟件下載到計(jì)算機(jī)中就可以了!不需要相互轉(zhuǎn)換。我們用匯編寫(xiě)程序也是一樣的方法。不過(guò)所生成的目標(biāo)文件其格式可以是.Hex文件或者.bin文件,只不過(guò)前者以十六進(jìn)制形式出現(xiàn)而后者以二進(jìn)制形式出現(xiàn)!我們可以類推出其它編程語(yǔ)言都應(yīng)該大同小異。
當(dāng)然,最后我們可以使C語(yǔ)言和匯編語(yǔ)言相互轉(zhuǎn)化的,我們稱為反匯編!這種方法用的場(chǎng)合較少!以上是我對(duì)這個(gè)問(wèn)題的回答!歡迎朋友們參與討論這個(gè)話題。敬請(qǐng)觀注電子及工控技術(shù)!
用C寫(xiě)的程序才會(huì)被編譯成匯編語(yǔ)言,再由匯編器翻譯成機(jī)器碼。是這樣的,要搞清楚一點(diǎn),只有機(jī)器語(yǔ)言才能和底層硬件打交道,也就是每種高級(jí)語(yǔ)言的最終執(zhí)行代碼都是機(jī)器碼,至于中間形式的目標(biāo)代碼則有很多種,像Java語(yǔ)言的代碼被編譯成一種叫字節(jié)碼的中間代碼,然后由相應(yīng)平臺(tái)的JVM翻譯成機(jī)器碼執(zhí)行,還有的像JavaScript, Python這種解釋型的語(yǔ)言根本就不經(jīng)過(guò)編譯,而是逐條代碼直接翻譯成機(jī)器碼再執(zhí)行。很多時(shí)候說(shuō)C語(yǔ)言是最基礎(chǔ)的語(yǔ)言并不是說(shuō)所有語(yǔ)言都要往C語(yǔ)言上走一遍,一部分原因是因?yàn)镃語(yǔ)言的特性使得它適合編寫(xiě)一些語(yǔ)言的底層支持模塊,還有就是操作系統(tǒng)是由C語(yǔ)言編寫(xiě)的,應(yīng)用程序與操作系統(tǒng)交互需要調(diào)用一些C寫(xiě)的模塊。總之,你完全可以自己發(fā)明一種語(yǔ)言,你定義好語(yǔ)法規(guī)則,再寫(xiě)一個(gè)針對(duì)這種需要的編譯器,能翻譯成平臺(tái)兼容的機(jī)器語(yǔ)言,那樣就可以執(zhí)行了。所以不存在什么語(yǔ)言都要轉(zhuǎn)成C這一說(shuō)
java是編譯成軟指令,運(yùn)行時(shí)由jvm就是java虛擬機(jī)翻譯成本地硬件指令。而c語(yǔ)言編譯后是直接的機(jī)器指令。
因此,在不同架構(gòu)CPU和操作系統(tǒng)上實(shí)現(xiàn)不同的jvm就可以實(shí)現(xiàn)java庫(kù)的跨平臺(tái)使用。就是說(shuō)只要你的平臺(tái)能安裝jvm就能運(yùn)行java程序。
匯編語(yǔ)言是文本化的機(jī)器指令,任何語(yǔ)言一般不編譯成匯編語(yǔ)言。
而匯編語(yǔ)言程序,編譯之后才是機(jī)器指令。
機(jī)器指令都是二進(jìn)制存儲(chǔ)的,具體每條指令形態(tài)就像:指令id+參數(shù),參數(shù)就像是寄存器、立即數(shù)這樣的。指令id就是某條指令唯一的標(biāo)識(shí)。
指令id一般由CPU廠商給出列表,說(shuō)明哪個(gè)數(shù)字對(duì)應(yīng)于哪個(gè)指令。
所以很多涉及底層的東西是用C寫(xiě)的,既有較高性能,又相對(duì)容易移植。
比如Python是用C語(yǔ)言寫(xiě)的,但是Python程序在運(yùn)行時(shí)不會(huì)被編譯成C語(yǔ)言。
C++是給C語(yǔ)言添加了面向?qū)ο蟮母拍睢?/p>
實(shí)際情況并不是這樣的。
我們來(lái)一條條的看吧:
1.編程語(yǔ)言并不是都要編譯成C,這個(gè)說(shuō)法不知道是誰(shuí)提出來(lái)的。C語(yǔ)言和java,python一樣,也是一種高級(jí)語(yǔ)言。但是C語(yǔ)言有個(gè)優(yōu)勢(shì),就是可以嵌套匯編,實(shí)現(xiàn)底層交互。
2.很多人存在一個(gè)誤區(qū),匯編語(yǔ)言就是最底層的語(yǔ)言了,其實(shí)計(jì)算機(jī)還是不認(rèn)識(shí)匯編是個(gè)什么東西, 因?yàn)橛?jì)算機(jī)只能識(shí)別一種語(yǔ)言,那就是機(jī)器語(yǔ)言,全都是0和1這樣的二進(jìn)制數(shù)。比方說(shuō)MOV AX,BX,這條匯編的意思是寄存器將BX的內(nèi)容轉(zhuǎn)移到AX中,對(duì)應(yīng)機(jī)器語(yǔ)言的1000100111011000,非常復(fù)雜??梢赃@么理解:匯編語(yǔ)言實(shí)際上是機(jī)器指令的一種簡(jiǎn)寫(xiě)形勢(shì)。
可以看看下圖,做一個(gè)簡(jiǎn)單的加法運(yùn)算,分別用C語(yǔ)言,匯編和機(jī)器語(yǔ)言表示,各位就能理解高級(jí)語(yǔ)言的好處了。
無(wú)論是C還是其他什么語(yǔ)言,最終的目的都是實(shí)現(xiàn)某種功能,而這離不開(kāi)硬件的支持,所以歸根結(jié)底,還是要從程序和硬件的關(guān)系說(shuō)起。
在計(jì)算機(jī)中存在著大量的電子元件,電路就兩種情況:開(kāi)和關(guān),這兩種狀態(tài)分別用1和0來(lái)表示,也就是眾所周知的二進(jìn)制, 在計(jì)算機(jī)中,所有的運(yùn)算都是以二進(jìn)制的形式進(jìn)行的,比方說(shuō)3+4,在CPU中的表現(xiàn)形式為00110000+01000000,如果是負(fù)數(shù)運(yùn)算或浮點(diǎn)運(yùn)算的話還會(huì)更復(fù)雜一些,這里不再累述。
通過(guò)0和1決定電路狀態(tài),那么操作者是通過(guò)編程語(yǔ)言操作硬件的呢?這里編譯器的作用就體現(xiàn)出來(lái)了。
編譯器就像是一個(gè)翻譯一樣,也就任人們口中俗稱的編程軟件。其實(shí)用更專業(yè)的話來(lái)說(shuō),編譯器實(shí)際上是IDE(開(kāi)發(fā)集成環(huán)境)中的一個(gè)工具。
雖然大家可能在操作中感受不是那么強(qiáng)烈,但如果你在windows控制臺(tái)下用javac編譯過(guò)java代碼,或者你在linux的命令行下編譯過(guò)程序,這個(gè)時(shí)候我們就直接使用了編譯器。
最后提一點(diǎn),并不是所有的語(yǔ)言都會(huì)被編譯器直接編譯成機(jī)器語(yǔ)言。C語(yǔ)言是一個(gè)比較特殊的語(yǔ)言,因?yàn)樗侵苯涌梢灾苯泳幾g為匯編再編譯為機(jī)器碼的,這也是C語(yǔ)言可以和底層打交道的主要原因。像我們熟知的java,它會(huì)把源碼先轉(zhuǎn)換成一種叫做字節(jié)碼的東西,通過(guò)JVM虛擬機(jī)將其轉(zhuǎn)換為機(jī)器碼。所以說(shuō),編譯器的類型有很多,作用不能一概而論。
我認(rèn)為只是一小部分很low的高級(jí)語(yǔ)言需要C語(yǔ)言中轉(zhuǎn)編繹。大多數(shù)PC機(jī)語(yǔ)言直接編繹成宏匯編語(yǔ)言或者更直接的機(jī)器語(yǔ)言的形式執(zhí)行;而更低級(jí)的單片機(jī)語(yǔ)言,無(wú)論是C還是匯編,都直接編繹為機(jī)器碼,直接可被設(shè)備識(shí)別。
像高級(jí)的C++語(yǔ)言,如果進(jìn)行調(diào)試,基本以宏匯編語(yǔ)言的形式出現(xiàn),VC,VS等基本如此。當(dāng)然,要讓機(jī)器識(shí)別,最后必然是機(jī)器碼。
也有很多很low的高級(jí)語(yǔ)言,必須先轉(zhuǎn)為C,再執(zhí)行。這種語(yǔ)言效率都很低,可以理解為2次開(kāi)發(fā)。這也是為什么很多小公司、個(gè)人都可以做編繹器,小范圍、特定場(chǎng)合用用可以。
對(duì)于單片機(jī)編程,簡(jiǎn)單很多。這也是為什么單片機(jī)編繹程序眾多的主要原因。特別是最low的51系到8位程序的編繹,初學(xué)者都可以嘗試做。當(dāng)然做到極致也是不容易的。
編繹語(yǔ)言,要做到微軟MS的VC,VS的水平,估計(jì)需要幾十年的努力,同時(shí)還要對(duì)處理器的硬件結(jié)構(gòu)搞的很明白。如果相關(guān)的知名處理器廠商,如Intel,ARM架構(gòu)等廠商不跟你合作,估計(jì)永遠(yuǎn)也做不出來(lái)。
(圖片來(lái)源于網(wǎng)絡(luò),僅為示意)
首先這種認(rèn)識(shí)是不正確的,所有的編程語(yǔ)言要轉(zhuǎn)化成機(jī)器語(yǔ)言然后才去運(yùn)行,肯定不會(huì)轉(zhuǎn)化成C語(yǔ)言去運(yùn)行,因?yàn)镃語(yǔ)言還是要匯總成機(jī)器語(yǔ)言去運(yùn)行。從類別上講編程語(yǔ)言主要分成兩種,一種在運(yùn)行之前提前生成二進(jìn)制文件,機(jī)器上電直接運(yùn)行就可以了;另外一種是一邊運(yùn)行一邊編譯最終的結(jié)果也是轉(zhuǎn)化成二進(jìn)制文件,這樣機(jī)器才能準(zhǔn)確的識(shí)別出來(lái)。只要是遵循馮諾依曼的架構(gòu)都要轉(zhuǎn)化成二進(jìn)制的文件讓機(jī)器去運(yùn)轉(zhuǎn)執(zhí)行,可能在未來(lái)量子計(jì)算機(jī)發(fā)展起來(lái)架構(gòu)會(huì)有所改變,起碼在很長(zhǎng)的一段時(shí)間內(nèi)還會(huì)繼續(xù)這種架構(gòu)存在。
就拿典型的編程語(yǔ)言Java作為例子來(lái)闡述,java屬于典型的跨平臺(tái)語(yǔ)言,跨平臺(tái)的語(yǔ)言編程的好處是在任何一個(gè)操作系統(tǒng)上完成代碼編寫(xiě)之后就可以在任何平臺(tái)來(lái)運(yùn)行,一般開(kāi)發(fā)java都在windows平臺(tái)上開(kāi)發(fā),但真正在部署的時(shí)候既可以在linux服務(wù)器上運(yùn)行,也可以在windows服務(wù)器上運(yùn)行。但在實(shí)際運(yùn)行過(guò)程中還是要遵循各自的指令體系,做這個(gè)時(shí)期的關(guān)鍵點(diǎn)就在于java的虛擬機(jī),所以在安裝運(yùn)行環(huán)境的時(shí)候,不同的操作系統(tǒng)的版本是不一致的,java在編譯的時(shí)候都會(huì)轉(zhuǎn)化成字節(jié)碼,字節(jié)碼在不同操作系統(tǒng)的虛擬機(jī)都可以運(yùn)行,虛擬機(jī)里面會(huì)把主流的操作系統(tǒng)指令都分別集成進(jìn)去,也就是java版本在更迭的時(shí)候除了增加基本語(yǔ)法之外,還要在修改不同的操作系統(tǒng)底層實(shí)現(xiàn),這樣子才能真正做到跨平臺(tái)運(yùn)行。
跨平臺(tái)在嵌入式領(lǐng)域使用的比較多,如果用C++開(kāi)發(fā)的代碼可以在宿主機(jī)上直接運(yùn)行,調(diào)試完畢了然后再燒錄到嵌入式板卡里面去運(yùn)行,畢竟直接在PC的linux上調(diào)試代碼,要比直接在板子里調(diào)試要方便的多,這就是跨平臺(tái)的好處,除了方便開(kāi)發(fā)代碼還極大方便調(diào)試。
雖然不是每種編程語(yǔ)言都要轉(zhuǎn)化成C語(yǔ)言再去運(yùn)行,但很多編程語(yǔ)言的底層都是C語(yǔ)言來(lái)實(shí)現(xiàn)的,這是真實(shí)存在的事實(shí),但還是有很多人覺(jué)得C語(yǔ)言已經(jīng)過(guò)時(shí)了,起碼從招聘的簡(jiǎn)章C語(yǔ)言的比例在下降,這個(gè)主要原因是國(guó)內(nèi)程序員大部分都做著互聯(lián)網(wǎng)的工作,互聯(lián)網(wǎng)主要從事應(yīng)用級(jí)開(kāi)發(fā)比較多,所以一些集成化編程語(yǔ)言在國(guó)內(nèi)比較能夠吃得開(kāi),像python,php,Java在國(guó)內(nèi)都有著良好的生態(tài)基礎(chǔ),這是國(guó)內(nèi)軟件環(huán)境決定的。
C語(yǔ)言在很多基礎(chǔ)領(lǐng)域還是首選的編程語(yǔ)言,語(yǔ)法簡(jiǎn)潔但靈活多樣,在實(shí)際功能的的時(shí)候效率高性能強(qiáng),在很多領(lǐng)域還是第一編程語(yǔ)言,主要在通訊行業(yè)以及嵌入式領(lǐng)域還是C語(yǔ)言的主戰(zhàn)場(chǎng)。非常流行的人工智能在底層很多功能實(shí)現(xiàn)也是基于C語(yǔ)言完成,只不過(guò)C語(yǔ)言的角色從前臺(tái)走向了幕后,但重要性并沒(méi)有因此而降低,即使選擇C語(yǔ)言作為入門(mén)的編程語(yǔ)言,在基礎(chǔ)過(guò)硬的情況下照樣能找到合適的工作,不要迷信C語(yǔ)言已經(jīng)不行了之類的話語(yǔ)。
對(duì)于編程語(yǔ)言的編譯原理以及運(yùn)行過(guò)程還是要多去涉獵,如果純正的高級(jí)編程語(yǔ)言可能很少去關(guān)系這些個(gè)原理,都是去操心如何架構(gòu)等方面的問(wèn)題,底層如何實(shí)現(xiàn)功能對(duì)于上層調(diào)用方式都有決定性因素,盡管很多人不推薦第一門(mén)編程語(yǔ)言選擇C語(yǔ)言,但從個(gè)人的角度出發(fā),還是建議C語(yǔ)言作為入門(mén)編程語(yǔ)言,即使不是從事這方面的工作,起碼在從知識(shí)體系上還是有很大的好處,希望能幫到你。
你可能聽(tīng)錯(cuò)了 大部分編程語(yǔ)言是用C開(kāi)發(fā)的 例如大行其道的Python和Java 但是并不是說(shuō)這些語(yǔ)言一定會(huì)編譯為C 往往是編譯成虛擬機(jī)指令或者編譯成機(jī)器語(yǔ)言 注意機(jī)器語(yǔ)言不是匯編語(yǔ)言 機(jī)器語(yǔ)言是二進(jìn)制可以直接執(zhí)行
以Python為例 Python本身的編譯器會(huì)把源代碼編譯為虛擬機(jī)的指令 再用虛擬機(jī)去執(zhí)行這些指令 Python的一大優(yōu)勢(shì)是很容易呼叫C規(guī)格的共享函數(shù)庫(kù) 或者說(shuō)很容易用C來(lái)擴(kuò)展或者加速Python
單純就虛擬機(jī)來(lái)比較 Java虛擬機(jī)更優(yōu)秀 很多新技術(shù) 例如JIT編譯技術(shù)
不過(guò)Python+C的組合還是很有市場(chǎng) 特別是人工智能和工業(yè)控制 還有自動(dòng)化部署和測(cè)試自動(dòng)化
如果你想了解虛擬機(jī) SQLite虛擬機(jī)相對(duì)比較簡(jiǎn)單 雖然是個(gè)數(shù)據(jù)庫(kù)虛擬機(jī) 不過(guò)文檔比較容易消化 代碼也就一個(gè)C文件
計(jì)算機(jī)能夠識(shí)別的語(yǔ)言只有機(jī)器語(yǔ)言,也就是0-1代碼,咱們可以直接編寫(xiě)0-1代碼讓計(jì)算機(jī)執(zhí)行程序,沒(méi)有問(wèn)題。但是,這就增加我們學(xué)習(xí)編程的學(xué)習(xí)成本和編程成本,為此咱們的前輩們開(kāi)發(fā)了編程語(yǔ)言,最早的是匯編語(yǔ)言,通過(guò)編譯器將匯編語(yǔ)言變成機(jī)器語(yǔ)言;另外一種是高級(jí)語(yǔ)言,像Python、java、C++、MATLAB、VB等等。高級(jí)語(yǔ)言接近于人類語(yǔ)言,學(xué)習(xí)成本、編程成本低,匯編語(yǔ)言學(xué)習(xí)成本較高,目前除了對(duì)計(jì)算速度要求極其嚴(yán)格的環(huán)境,一般不采用匯編語(yǔ)言。
因此,高級(jí)語(yǔ)言和匯編語(yǔ)言不是遞進(jìn)的關(guān)系,即高級(jí)語(yǔ)言不會(huì)編譯成匯編語(yǔ)言,他們都會(huì)變成機(jī)器語(yǔ)言(即0-1代碼),供計(jì)算機(jī)硬件識(shí)別并驅(qū)動(dòng)計(jì)算機(jī)完成相關(guān)功能。
10
回答6
回答0
回答0
回答1
回答0
回答5
回答3
回答10
回答0
回答