10.23 硅谷創業之父解析編程語言

作者:Paul Graham,美國著名程序員,風險投資家,Y Combinator創始人。本文選自他享譽世界的《黑客與畫家》一書。

所有機器都有一張操作命令清單,讓你可以控制它。有時這個清單非常簡短。電水壺就只允許兩種操作:打開和關閉。CD播放器稍微複雜點,除了打開和關閉以外,還能調節音量、播放、暫停、快進、快退、隨機播放等。

計算機和其他機器一樣,也有一張操作命令清單。比如,可以命令計算機把兩個數相加。這種操作命令的總和就是計算機的機器語言(machine language)。

硅谷創業之父解析編程語言

機器語言

計算機剛發明的時候,所有程序就是一條條機器語言的命令。沒過多久,程序就改成使用匯編語言了,它要比機器語言寫起來稍微方便一點。命令清單還是一樣的,就是每個命令換了一個更人性化的名字。機器語言的加法命令是11001101,這可能就是計算機內部的加法表達方式,但是在彙編語言中,這條命令就改成了add。

機器語言和彙編語言的共同問題就是,只能讓大多數計算機做一些很簡單的事情。比如,假定你想讓計算機的蜂鳴器響10次,但是不存在一條直接的機器語言命令讓電腦重複進行n次操作,所以只能用機器語言寫出下面這樣的程序:

硅谷創業之父解析編程語言

如果只是為了讓蜂鳴器響10次就不得不寫這麼多代碼,不難想象寫出一個文字處理器或電子表格將是一項多麼浩大的工程。

順便說一句,請再看一下上面的程序。蜂鳴器真的會響10次嗎?不,響了11次。我不應該在第一行使用10,而應該使用9。我故意在這個例子中留了一個bug,證明編程語言的一個重要特點:一個操作所需的代碼越多,就越難避免bug,也越難發現它們。

高級語言

現在假設你不得不用匯編語言開發程序,但是你有了一個助手,他可以幫你承擔那些麻煩的髒活。所以,你只要把程序寫成下面這樣就行了①:

硅谷創業之父解析編程語言

① dotimes是Lisp語言中表示循環處理的命令。——譯者注

接下來,你的助手會用匯編語言來實現這條命令(假定他不會產生bug)。

事實上大多數程序員就是這樣工作的,不同之處就是,程序員的助手不是一個人,而是編譯器。所謂“編譯器”,本身就是一個程序,作用是將簡便方式書寫的程序(就像上面這一行命令)轉變為硬件可以理解的語言。

這種簡便方式書寫的程序所使用的語言就叫做高級語言。它讓你能夠使用更強大的命令開發程序,比如現在你就有了“重複n次操作”的命令,不再僅限於只能做簡單的“兩個數相加”。

寫程序時有了方便的命令,就可以把程序寫得更簡短。在上面假想的例子中,高級語言寫出來的程序的長度只有機器語言的五分之一。所以,要是你犯錯了,現在也更容易發現。

高級語言還有一個優點,它使得程序更具有可移植性。不同計算機的機器語言都不是完全相同的。所以,你無法將為某一種機型寫的機器語言程序放到另一種機型上運行,只有徹底重寫才能實現。但是,如果你的程序是用高級語言寫的,你只需要重寫編譯器就可以了。

編譯器不是高級語言唯一的實現方法,另一種方法是使用解釋器,它的作用是實時地將代碼解釋為相應的機器語言,然後一行行運行。相比之下,編譯器則是先將整個程序全部翻譯成機器語言,然後再運行。

開放源碼

編譯器處理的高級語言代碼又叫做源碼。它經過翻譯以後產生的機器碼就叫做目標碼。顧客購買市場上的商業軟件時得到的往往只是目標碼。(目標碼很難讀懂,所以相當於被加密了,可以保護公司的商業秘密。)但是,後來出現另一種潮流:開放源碼的軟件。你可以得到源碼,並且可以不受限制地修改它。

這兩種方式的真正區別在於,開放源碼使你對軟件有更大的控制權,如果你想理解開源軟件如何運行,只要閱讀源碼就行了。如果願意,你甚至可以修改軟件、重新編譯。

你之所以需要這樣做,一個原因可能是為了修正bug。比如,你自己不可能修正Windows的bug,因為你沒有源碼。(理論上你也許可以破解目標碼,但是實際上這是非常難的。另一方面,軟件的授權協議一般也不允許你這樣做。)這會導致很大的問題。一旦Windows出現新的安全漏洞,只能等待微軟公司發佈解決方法,這還算是快的。如果bug的危害性不嚴重,只是偶爾會讓你的機器死機,那麼可能不得不等到下一次全面升級後問題才會得到解決。

開放源碼的優勢還不僅侷限於可以自己動手解決bug。這裡的關鍵是所有人都可以參與。所以,開源軟件就像一篇經受同行評議的論文。許許多多的聰明人仔細閱讀了Linux和FreeBSD這樣的開源操作系統的源碼,發現並且解決了大量的bug。相比之下,Windows的可靠性只能依賴於大公司自己的質量保證部門了。

開放源碼的擁護者常常被看作反對知識產權的怪人。其中有些人確實如此,但是我本人肯定不反對知識產權。只是如果你要我安裝沒有源碼的軟件,我會非常猶豫。普通的消費者也許不需要看到他們使用的文字處理器的源碼,但是在非常強調軟件可靠性的情況下,出於強烈的工程需求的考慮,會要求開放源碼。

語言的戰爭

絕大多數程序員在絕大多數時候都使用高級語言編程。現在很少有人使用匯編語言。程序員的時間要比計算機的時間昂貴得多,後者已經變得很便宜了,所以幾乎不值得非常麻煩地用匯編語言開發軟件。只有少數最關鍵的部分可能還會用到彙編語言,比如開發某個計算機遊戲時,你需要在微觀水平控制硬件,使得遊戲速度得到最大限度的終極提高。

Fortran、Lisp、Cobol、Basic、C、Pascal、Smalltalk、C++、Java、Perl和Python,全都是高級語言。它們只是比較出名的幾種而已。現在的高級語言大概有幾百種之多。不同機器語言的指令集基本相同,但是高級語言就不一樣,它們開發程序的模式差別相當大。

那麼,應該使用哪一種語言?嗯,關於這個問題,現在有很多爭論。部分原因是,如果你長期使用某種語言,你就會慢慢按照這種語言的思維模式進行思考。所以,後來當你遇到其他任何一種有重大差異的語言,即使那種語言本身並沒有任何不對的地方,你也會覺得它極其難用。缺乏經驗的程序員對於各種語言優缺點的判斷經常被這種心態誤導。

可能因為想炫耀自己見多識廣,某些黑客會告訴你所有高級語言基本相似。“所有編程語言我都用過。”某個看上去飽經風霜又酷的黑客往酒吧裡一坐,“你用什麼語言並不重要,重要的是你對問題是否有正確的理解。代碼以外的東西才是關鍵。”

這當然是一派胡言。各種語言簡直是天差地別,比如Fortran I和最新版的Perl就是兩種完全不同的語言,而早期版的Perl和最新版的Perl之間的差別也大得驚人。但是,那個誇誇其談的黑客可能真的相信自己的這番話,的確有可能使用所有不同的語言寫出了與用原始的Pascal語言寫的差不多的程序。如果你吃過麥當勞,就會知道全世界各地的麥當勞的味道都幾乎一樣。

一些黑客只喜歡自己用的語言,反感其他所有的語言。另一些黑客則說所有的語言都一樣。事實介於這兩個極端之間。語言之間確實有差別,但是很難確定地說哪一種語言是最好的。這個領域依然還在快速發展。

抽象性

高級語言比彙編語言更接近人類語言,而某些高級語言又比其他語言更進一步。舉例來說,C語言是一種低層次語言,很接近硬件,幾乎堪稱可移植的彙編語言,而Lisp語言的層次則是相當高。

如果高層級語言比彙編語言更有利於編程,你也許會認為語言的層次越高越好。一般情況下確實如此,但不是絕對的。編程語言可以變得很抽象,完全脫離硬件,但也有可能走錯了方向。比如,我覺得Prolog語言就有這個問題。它的抽象能力強得不可思議,但是隻能用來解決2%的問題,其餘時間你苦思冥想、運用這些抽象能力寫出來的程序實際上就是Pascal語言的程序。

另一個你會用到低層次語言的原因就是效率問題。如果你非常關注運行速度,那麼最好使用接近機器的語言。大多數操作系統都是用C語言寫的,這並非偶然。不過,硬件的運行速度越來越快了,所以使用C這樣的低層次語言開發應用程序的必要性正在不斷減少,但是大家似乎還是要求操作系統越快越好。(另一種可能是,人們還是希望“緩存區溢出攻擊”繼續存在下去,以便讓大家時時保持警惕。②)

② 最常見的幾種入侵計算機的手法都是利用了C語言的某些特點。當你在C語言中為輸入的內容分配出一片內存(也叫“緩存”)時,它會被分配在當前運行代碼的返回地址旁邊。所謂“返回地址”指的是一塊特定內存,當前代碼運行完畢以後,就要運行這塊內存中包含的代碼。也就是說,它實際上是計算機下一步要做的事情。

假定有人打算入侵你的計算機,他們猜出你會為某種輸入分配256字節的緩存,於是他們就提交多於256字節的內容,目的是覆蓋旁邊的“返回地址”。那麼,當前代碼運行完畢之後,程序的控制權就交給了他們指定的內存地址。這個地址通常是緩存的首地址,緩存中是入侵者事前編好的機器碼。於是,入侵者的程序就運行在你的計算機上了。

如果使用更抽象的高級語言,上面的事情是不可能發生的。但是,在C語言中,一旦接受用戶輸入的時候你沒有檢查輸入長度,就創造出了一個安全漏洞。利用這種漏洞的攻擊行為就被稱為“緩衝區溢出攻擊”。在這種攻擊中,還有其他方法可以控制計算機,但是覆蓋返回地址是最經典的一種。

有意思的是,劫持飛機與“緩衝區溢出攻擊”有類似之處。在一般飛機上,乘客區與駕駛艙是相通的,就好像C語言中數據區與代碼區是相鄰的一樣。劫機者一旦進入駕駛艙,實際上就相當於把自己從數據提升為代碼。

安全帶還是手銬?

語言設計者之間的最大分歧也許就在於,有些人認為編程語言應該防止程序員幹蠢事,另一些人則認為程序員應該可以用編程語言幹一切他們想幹的事。Java語言是前一個陣營的代表,Perl語言則是後一個陣營的代表。(美國國防部很看中Java也就不足為奇了。)

自由語言派的信徒嘲笑另一方是“B&D”(奴役和戒律,Bondage and Discipline)語言,很無禮地暗示用那些語言編程的人是下等人。我不知道對方如何反擊這些喜歡Perl的自由派,也許他們不喜歡給別人起綽號,因此我就無從知道。

由於防止程序員做蠢事有好幾種方法,所以上面的爭論逐漸分化成幾個較小的議題。目前最活躍的議題之一就是靜態類型語言與動態類型語言之爭。在靜態類型語言中,寫代碼時必須知道每個變量的類型。而在動態類型語言中,隨便什麼時候,你都可以把變量設為任意類型的值。

靜態類型語言的擁護者認為這樣可以防止bug,並且幫助編譯器生成更快的代碼(這兩點理由都成立)。動態類型語言的擁護者認為靜態類型對程序構成了限制(這點理由也成立)。我本人更喜歡動態類型,痛恨那些限制我的自由的語言。但是,確實有一些很聰明的人看來喜歡用靜態類型語言。所以,這個問題依然值得討論,並沒有固定答案。

面向對象編程

眼下另一個爭論的熱點則是面向對象編程。它是一種不同的組織程序的方法。假定你要寫一個程序,計算二維圖形的面積。首先,你必須知道到底是圓形還是正方形。一種解決方法是用一整塊的代碼判斷遇到的是什麼圖形,然後再用相應的公式計算面積。面向對象編程不是這樣,它的方法是寫出兩個類,一個是圓形類,另一個是正方形類,然後每個類裡面用一小塊代碼(叫做方法)計算該類圖形的面積。求面積的時候,你就問要用哪一個類,然後再使用相應的方法得出最後答案。

這兩種不同的計算方法可能聽上去很相似,事實上,運行代碼後,實際計算面積的運算過程也很相似。(這不奇怪,因為你本來就在解決同一個問題。)但是,代碼的形式卻是大相徑庭。在面向對象編程的方式中,計算圓面積和正方形面積的代碼可能分散在不同的文件中。與圓形有關的代碼都放在一個文件中,與正方形有關的代碼則放在另一個文件中。

面向對象編程的優點在於,如果你需要修改程序,計算另一種圖形的面積,比如三角形,你只需要再另外增加一塊相應的代碼就可以了,甚至可以不修改程序的其他部分。但是,批評者會反駁說,這種方法的缺點是,由於增加代碼不用考慮其他部分,結果往往導致寫出性能不佳甚至有副作用的代碼,就好比造房子不考慮已經完成的部分一樣。

關於面向對象編程優劣的爭論並不像靜態類型與動態類型之爭那樣壁壘分明,因為編程的時候你只能在靜態類型和動態類型之中選一種。但是,面向對象編程只是程度不同的問題。事實上有兩種程度的面向對象編程:某些語言允許你以這種風格編程,另一些語言則強迫你一定要這樣編程。

我覺得後一類語言不可取。允許你做某事的語言肯定不差於強迫你做某事的語言。所以,至少在這方面我們可以得到明確的結論:你應該使用允許你面向對象編程的語言。至於你最後到底用不用則是另外一個問題了。

文藝復興

有一件事,我想所有軟件業的人都會同意,那就是最近出現了很多新的編程語言。直到20世紀80年代,只有大機構才買得起開發編程語言所需的硬件,所以大多數編程語言都是大公司的教授或者研究員開發的。而現在,一個高中生就能搞到所有必需的硬件。

Perl語言的設計者拉里 · 瓦爾③的例子啟發了很多黑客:為什麼不動手設計一種自己的語言呢?只要你懂得駕馭開源軟件社區,就會有很多人在短期內為你提供大量的代碼。

③ Larry Wall(1954—)在大學裡主修語言學。1987年為了使管理機房的工作變得方便,他在業餘時間創造了Perl語言。——譯者注

結果就是產生了一些也許可以稱為“頭重腳輕”的語言:它們的內核設計得並非很好,但是卻有著無數強大的函數庫,可以用來解決特定的問題。(你可以想象一輛本身性能很差的小汽車,車頂卻綁著一個飛機發動機。)有一些很瑣碎、很普遍的問題,程序員本來要花大量時間來解決,但是有了這些函數庫以後,解決起來就變得很容易,所以這些庫本身可能比核心的語言還要重要。所以,這些奇特組合的語言還是蠻有用的,一時間變得相當流行。車頂上綁著飛機發動機的小車也許真能開,只要你不嘗試拐彎,可能就不會出問題。④

④ 提醒各位親愛的黑客,我只是打一個比方,請不要嘗試在車頂綁上飛機發動機。

另外,可以認為這類“頭重腳輕”的語言存在已久,Fortran語言的流行主要就是因為它的函數庫。

另一個結果就是語言的多樣化。編程語言之間總是存在很大區別。Fortran、Lisp、APL都是1970年以前開發出來的,它們之間的區別大得就像海星、熊、蜻蜓之間的區別。新興的開源編程語言肯定將繼承這種傳統。

現在好像每隔一段日子就能聽到一種新出現的語言。喬納森 · 埃裡克森把這種現象稱為“編程語言的文藝復興”。人們有時還會用另一個說法,即“編程語言的戰爭”。這並不矛盾,文藝復興時期就是存在很多戰爭的。

實際上,很多歷史學家相信戰爭是文藝復興的一個副產品。⑤當時,歐洲活力旺盛可能就是因為它分成許多互相競爭的小國。它們互相毗鄰,所以新思想能夠從一個國家傳播到另一個國家,但是它們又互相獨立,使得單個的統治者無法遏制創新的發展。相比之下,中國古代的封建皇朝禁止民間建造大型的遠洋船隻,阻止了經濟的正常發展。

⑤ 參見Carlo Cipolla所著的《槍,帆船,帝國:技術革新在1400~1700年歐洲擴張早期階段的作用》(Guns, Sails, and Empires: Technological Innovation and the Early Phases of European Expansion 1400-1700),Pantheon,1965年出版。

所以,程序員活在這個文藝復興時代可能是一件好事。如果我們所有人都使用同一種編程語言,反而有可能是壞事。

推薦閱讀:《黑客與畫家——來自計算機時代的遠見》

硅谷創業之父解析編程語言

《黑客與畫家——來自計算機時代的遠見》 作者:Paul Graham 譯者:阮一峰

  • 美國互聯網界如日中天的教父、哈佛大學離經叛道的博士保羅·格雷厄姆著作!一本書,你不曾想到的視角,徹底顛覆你對世界的認識!美國亞馬遜、紐約時報超級暢銷書,國內各大網店IT人文類榜首圖書!

這本書是硅谷創業鉅子保羅·格雷厄姆的文集。雖然黑客從外表看上去一般都是呆呆的,但是他們的大腦內部卻是一個有趣得讓你吃驚的地方。翻開本書,隨著硅谷創業與技術大師敏感而豐富的內心,重新認識你所身處的世界。內容涉及思想意識、設計、互聯網、IT技術,以及創業等。


分享到:


相關文章: