其實編譯這件事情從實現原理上看蠻蠢的,就是按套路寫匯編而已,編譯器作者提前把所有的語法都列舉出來,然后一種一種寫清楚每個語法對應到什么匯編上,比如說函數開頭固定是幾條匯編,遇到if翻譯成什么匯編,遇到for翻譯成什么匯編,遇到加法、減法各自是什么匯編等等。具體實現上現在可以分為直接到目標機器的匯編,和先到中間碼(如LLVM)再到目標機器匯編等。
所謂編譯優化,其實基本也是固定的,遇到什么條件則將之前的寫法換成另一個寫法,遇到什么條件可以將兩段匯編合成一段,等等。
條件堆砌的多了,反而顯得編譯出來得匯編很“聰明”了,其實都是按規則生成的,編譯器不會為你寫的程序“靈機一動”將它變成別的形式,但這種樸實的作風帶來的是絕對的正確性。
首先,指針在機器層面是非常簡單的東西。你不帶指針的操作可能是這樣:
intadd(inta,intb)
{
returna+b;
}
編譯成偽匯編:
從棧頂寄存器+偏移a的位置,取四個字節到通用寄存器1
從棧頂寄存器+偏移b的位置,取四個字節到通用寄存器2
在通用寄存器1和通用寄存器2執行四字節整數加法,結果在通用寄存器2
將通用寄存器2存儲四個字節,到棧頂+RESULT的位置
帶指針:
intadd(int*a,int*b)
{
return*a+*b;
}
編譯成偽匯編:
從棧頂寄存器+偏移a的位置,取八個字節到通用寄存器1
按照通用寄存器1的內容作為位置,取四個字節到通用寄存器1
從棧頂寄存器+偏移b的位置,取八個字節到通用寄存器2
按照通用寄存器2的內容作為位置,取四個字節到通用寄存器2
在通用寄存器1和通用寄存器2執行四字節整數加法,結果在通用寄存器2
將通用寄存器2存儲四個字節,到棧頂+RESULT的位置
引用很大程度上只是語法糖,實際編譯出來的實現可能是:
什么都不做,只是編譯限制。比如同作用域里的別名:
inta=1;
int&b=a;
就是個地址,比如作為成員、作為函數參數:
structvan
{
int&fuckyou;
}
voiddeep_dark_fantasy(int&ass_we_can);
對于C++,忽略RTTI和trycatch的事情,C++和C沒有實質上的區別:
類型(在運行時)并不存在,只是編譯器、語言標準給你的幻境。
類基本上就是結構體;
對象方法只不過是把對象實例作為隱藏參數的函數;
虛函數只不過是虛表、函數指針;
operator只不過是名字有點特別的函數;
模板實質上是代碼生成的過程。
那么這里就沒有什么神奇的地方了。
對于編譯過程,首先C++的編譯速度是臭名昭著的慢,快的只是編譯出來的程序運行快。至于為什么編譯出來的東西快,原因是多方面的:
C++本來就是用于開發性能敏感項目的,人家在寫程序的時候就會注意性能問題。
C++通常用于編譯到nativecode,直接由CPU執行,那么相比隔了一層解析器的語言通常會更快。
C++這種編譯與運行時分離的語言,可以在編譯時使用更耗時間的優化技術,相比運行時才編譯的腳本語言會快。
至于編譯器怎樣優化,你學了編譯原理就知道了(我并沒有學過)。大致上來講,現在的代碼是給人看的,會有很多對于機器邏輯是冗余的部分,編譯器會把這些冗余邏輯“收”起來。你可以看看GCC文檔的優化選項部分(
OptimizeOptions
?
gcc.gnu.org
),從中了解一個完備的現代編譯器有哪些優化內容。