天天看點

自己動手構造編譯系統:編譯、彙編與連結1.2 曆史淵源

<b>1.2  曆史淵源</b>

  

   曆史上很多新鮮事物的出現都不是偶然的,計算機學科的技術和知識如此,編譯系統也不例外,它的産生來源于程式設計工作的需求。程式設計本質上是人與計算機交流,人們使用計算機解決問題,必須把問題轉化為計算機所能了解的方式。當問題規模逐漸增大時,程式設計的勞動量自然會變得繁重。編譯系統的出現在一定程度上降低了程式設計的難度和複雜度。

  在計算機剛剛誕生的年代,人們隻能通過二進制機器指令指揮計算機工作,計算機程式是依靠人工撥動計算機控制台上的開關被輸入到計算機内部的。後來人們想到使用穿孔卡片來代替原始的開關輸入,用卡片上穿孔的有無表示計算機世界的“0”和“1”,讓計算機自動讀取穿孔卡片實作程式的錄入,這裡錄入的指令就是常說的二進制代碼。然而這種程式設計工作在現在看起來簡直就是一個“噩夢”,因為一旦穿孔卡片的制作出現錯誤,所有的工作都要重新來過。

  人們很快就發現了使用二進制代碼控制計算機的不足,因為人工輸入二進制指令的錯誤率實在太高了。為了解決這個問題,人們用一系列簡單明了的助記符代替計算機的二進制指令,即我們熟知的彙編語言。可是計算機隻能識别二進制指令,是以需要一個已有的程式自動完成彙編語言到二進制指令的翻譯工作,于是彙編器就産生了。程式員隻需要寫出彙編代碼,然後交給彙編器進行翻譯,生成二進制代碼。是以,彙編器将程式員從煩瑣的二進制代碼中解脫出來。

  使用彙編器提高了程式設計的效率,使得人們有能力處理更複雜的計算問題。随着計算問題複雜度的提高,程式設計中出現了大量的重複代碼。人們不願意進行重複的勞動,于是就想辦法将公共的代碼提取出來,彙編成獨立的子產品存儲在目标檔案中,甚至将同一類的目标檔案打包成庫。由于原本寫在同一個檔案内的代碼被分割到多個檔案中,那麼最終還需要将這些分離的檔案拼裝起來形成完整的可執行代碼。但是事情并沒有那麼簡單,由于檔案的子產品化分割,檔案間的符号可能會互相引用。人們需要處理這些引用關系,重新計算符号的引用位址,這就是連結器的基本功能。連結器使得計算機能自動把不同的檔案子產品準确無誤地拼接起來,使得代碼的複用成為可能。

  圖1-2描述的連結方式稱為靜态連結,但這種方式也有不足之處。靜态連結器把公用庫内的目标檔案合并到可執行檔案内部,使得可執行檔案的體積變得龐大。這樣做會導緻可執行檔案版本難以更新,也導緻了多個程式加載後相同的公用庫代碼占用了多份記憶體空間。為了解決上述的問題,現代編譯系統都引入了動态連結方式(見圖1-3)。動态連結器不會把公用庫内的目标檔案合并到可執行檔案内,而僅僅記錄動态連結庫的路徑資訊。它允許程式運作前才加載所需的動态連結庫,如果該動态連結庫已加載到記憶體,則不需要重複加載。另外,動态連結器也允許将動态連結庫的加載延遲到程式執行庫函數調用的那一刻。這樣做,不僅節約了磁盤和記憶體空間,還友善了可執行檔案版本的更新。如果應用程式子產品設計合理的話,程式更新時隻需要更新子產品對應的動态連結庫即可。當然,動态連結的方式也有缺點。運作時連結的方式會增加程式執行的時間開銷。另外,動态連結庫的版本錯誤可能會導緻程式無法執行。由于靜态連結和動态連結的基本原理類似,且動态連結器的實作相對複雜,是以本書編譯系統所實作的連結器采用靜态連結的方式。

               圖1-2  靜态連結                                   圖1-3  動态連結

  彙編器和連結器的出現大大提高了程式設計效率,降低了程式設計和維護的難度。但是人們對彙編語言的能力并不滿足,有人設想要是能像寫數學公式那樣對計算機程式設計就太友善了,于是就出現了如今形形色色的進階程式設計語言。這樣就面臨與當初彙編器産生時同樣的問題——如何将進階語言翻譯為彙編語言,這正是編譯器所做的工作。編譯器比彙編器複雜得多。彙編語言的文法比較單一,它與機器語言有基本的對應關系。而進階語言形式比較自由,計算機識别進階語言的含義比較困難,而且它的語句翻譯為彙編語言序列時有多種選擇,如何選擇更好的序列作為翻譯結果也是比較困難的,不過最終這些問題都得以解決。進階語言編譯器的出現,實作了人們使用簡潔易懂的程式設計語言與計算機交流的目的。

繼續閱讀