1. 程式人生 > >C++, Java和C#的編譯過程解析

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

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

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


圖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#是一門純編譯語言,且需要編譯兩次。)

 

圖2 C#的編譯過程

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


圖3 Windows API

JIT編譯的優點和缺點

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

    JIT的替代方案使用NGen

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


總結:

    在非託管環境中,我們需要知道編譯的過程分成編譯和連線兩個階段,編譯階段將源程式(*.c,*.cpp或*.h)轉換成為目的碼(*.o或*.obj檔案),至於具體過程就是上面說的C/C++編譯過程的前三個階段;連結階段是把前面轉成成的目的碼(obj檔案)與我們程式裡面呼叫的庫函式對應的程式碼連結起來形成對應的可執行檔案(exe檔案)。

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

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


來源:http://www.cnblogs.com/rush/p/3155665.html