[譯]C++, Java和C#的編譯過程解析


1.1.1 摘要

我們知道計算機不能直接理解高級語言,它只能理解機器語言,所以我們必須要把高級語言翻譯成機器語言,這樣計算機才能執行高級語言編寫的程序,在接下來的博文中,我們將介紹非托管和托管語音的編譯過程。

1.1.2正文

非托管環境的編譯過程(C/C++)

純C/C++的程序通常運行在一個非托管環境中,類是由頭文件(.h)和實現文件(.cpp)組成,每個類形成了一個單獨的編譯單元,當我們編譯程序時,幾個基本組件會把我們的源代碼翻譯成二進制代碼,接下來我們通過以下圖片說明非托管環境的編譯過程:

GCC_CompilationProcess

圖1 C/C++編譯過程

首先是預處理器,如果在項目中有頭文件和宏表達式,那么它將負責包含頭文件和翻譯所有的宏觀表達式。

接下來是編譯器,它不是直接生成二進制代碼,而是生成匯編代碼(.s),這基本上是所有現代的非結構化語言的共同基礎。

然后,匯編程序把匯編代碼翻譯成目標代碼(.o和.obj文件,機器指令)。

最后鏈接器,它把所有彼此相關的目標文件和生成的可執行文件或庫鏈接起來。

總而言之,在一般情況下,我們的代碼首先翻譯成匯編代碼,接着翻譯成機器指令(二進制代碼)。

什么是宏?

在C/C++中,宏是預處理指令,它有多種應用技術:包括預定義、創建關鍵字和條件編譯等等。在一般情況下,這些技術在C++中使用被認為是不好的做法,主要原因是有可能濫用C++提供的語法變化功能,甚至有可能在不知情情況下創建了非標准的語言,宏不遵循一般的源代碼編譯規則,由於它通過預處理來處理,而不是編譯器。

托管環境的編譯過程(C#/Java)

在托管環境中,編譯的過程略有不同,我們熟知的托管語言有C#和Java,接下來,我們將以C#和Java為例介紹在托管環境中的編譯過程。

當我們在喜愛的IDE中編寫代碼時,第一個檢測我們代碼的就是IDE(詞法分析),然后,編譯成目標文件和鏈接到動態/靜態庫或可執行文件進行再次檢查(語法分析),最后一次檢查是運行時檢查。托管環境的共同特點是:編譯器不直接編譯成機器碼,而是中間代碼,在.NET中稱為MSIL - Microsoft Intermediate Language,Java是字節碼(Bytecode)

在那之后,在運行時JIT(Just In Time)編譯器將MSIL翻譯成機器碼,這意味着我們的代碼在真正使用的時候才被解析,這允許在CLR(公共語言運行時)預編譯和優化我們的代碼,實現程序性能的提高,但增加了程序的啟動時間,我們也可以使用Ngen(Native Image Generator)預編譯我們的程序,從而縮短程序的啟動時間,但沒有運行時優化的優點。(JeffWong的補充Java是先通過編譯器編譯成Bytecode,然后在運行時通過解釋器將Bytecode解釋成機器碼;C#是先通過編譯器將C#代碼編譯成IL,然后通過CLR將IL編譯成機器代碼。所以嚴格來說Java是一種先編譯后解釋的語言,而C#是一門純編譯語言,且需要編譯兩次。)

 Dot_Net_Application_Compilation-707676

圖2 C#的編譯過程

.Net Framework就是在Win32 core上添加了一個抽象層,它提供的一個好處就是支持多語言、JIT優化、自動內存管理和改進安全性;另外一個完整解決方案是WinRT,但這涉及到另外一個主題了,這里不作詳細介紹。

MicrosoftBoxologyDiagram

圖3 Windows API

JIT編譯的優點和缺點

JIT編譯帶來了許多好處,最大的一個在我看來是性能的優勢,它允許CLR(通用語言運行時扮演Assembler組件)只執行需要的代碼,例如:假設我們有一個非常大的WPF應用程序,它不是立即加載整個程序,而是CLR開始執行時,我們代碼的不同部分將通過一個高效的方法翻譯成本地指令,因為它能夠檢查系統JIT和生成優化的代碼,而不是按照一個預定義的模式。不幸的是,有一個缺點就是啟動的過程比較慢,這意味着它不適用於加載時間長的包。

JIT的替代方案使用NGen

如果Visual Studio由JIT創建,那么它的啟動我們將需要等待幾分鍾,相反,如果它是使用Ngen(Native Image Generator)編譯,它將創建純二進制可執行文件,如果只考慮速度的問題,那是絕對是正確的選擇。

1.1.3總結

在非托管環境中,我們需要知道編譯的過程分成編譯和連接兩個階段,編譯階段將源程序(*.c,*.cpp或*.h)轉換成為目標代碼(*.o或*.obj文件),至於具體過程就是上面說的C/C++編譯過程的前三個階段;鏈接階段是把前面轉成成的目標代碼(obj文件)與我們程序里面調用的庫函數對應的代碼鏈接起來形成對應的可執行文件(exe文件)。

托管環境中,編譯過程可以分為:詞法分析、語法分析、中間代碼生成、代碼優化和目標代碼生成等等過程;無論是.NET還是Java,它們都會生成中間代碼(MSIL或Bytecode),然后把優化后的中間代碼翻譯成目標代碼,最后在程序運行時,JIT將IL翻譯成機器碼。

無論是托管或非托管語言,它們的編譯編譯過程是把高級語言翻譯成計算機能理解的機器碼,由於編譯過程涉及的知識面很廣(編譯的原理和硬件知識),而且本人的能力有限,也只能簡單的描述一下這些過程,如果大家希望深入了解編譯的原理,我推薦大家看一下《編譯原理》。

參考

[1] http://www.developingthefuture.net/compilation-process-and-jit-compiler/

更新:07/31/2013


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM