程序是怎么一步步變成機器指令的?
大家好,我是小風哥,今天簡單聊聊程序是怎么一步步變成機器指令的。
左邊是我們寫的代碼,右邊是CPU執(zhí)行的機器指令:
圖片
想讓CPU執(zhí)行代碼只需要簡單的點擊一下這個按鈕:
圖片
可是你知道這個按鈕的背后經(jīng)歷了哪些復(fù)雜的操作,你有沒有想過代碼是怎么一步步變成機器指令的?
程序員編寫的程序?qū)嶋H上就是一個字符串,必須得有個什么東西把字符串轉(zhuǎn)變從機器指令,它的輸入是字符串,輸出是01二進制機器指令,這就是編譯器。
圖片
編譯器本身就是一個程序,把人類認識的程序轉(zhuǎn)為CPU可以執(zhí)行的機器指令。
假設(shè)有這樣一段代碼:
圖片
這實際上就是一個字符串,編譯器要做的第一件事就是遍歷字符串并把有意義的字符組合提取出來,忽略掉空格換行等字符。
這里每一個字符組合實際上都有類型,比如int 和main都是關(guān)鍵字,0和5都是數(shù)字等,因此還需要標注好類型,這一步就是所謂的提取token。
圖片
提取出token之后還需要知道這些token組合在一起的含義是什么。
接下來遍歷所有token進行解析。
按照什么解析呢?答案是按照語法。
圖片
假設(shè)編譯器接下來發(fā)現(xiàn)token是if,那么很顯然,接下來會判定這是一個if語句,那么接下來就按照if語句的語法來解析。
圖片
編譯器在按照語法解析時會生成一顆樹,首先匹配的是if本身:
圖片
接下來是左括號:
圖片
括號之后是布爾表達式:
圖片
布爾表達式之后是右括號以及大的左括號。
接著是if內(nèi)部的語句:
圖片
注意看,根據(jù)語法解析token后生成的這棵樹就叫做抽象語法樹:AST。
接下來,編譯器遍歷這顆抽象語法樹并生成指令:
圖片
當然真正的編譯器可能并不會在這里直接生成機器指令。
我們知道CPU只能執(zhí)行一種類型的機器指令,x86處理器只能執(zhí)行x86機器指令,arm處理器只能執(zhí)行arm機器指令:
圖片
如果你發(fā)明了一種語言,為了適配不同的處理器自己需要針對每一種處理器編寫相應(yīng)的后端部分。
圖片
要是有一種工具能幫我們完成針對不同處理器的適配工作就好了,這就是LLVM,我們可以只生成針對LLVM的中間代碼,由LLVM處理剩下的部分。
圖片
這就是生成中間代碼的好處。
值得注意的是,編譯器在生成指令時會進行優(yōu)化,這個示例中變量a實際上沒什么用處,編譯器會注意到這一點并把針對變量a的賦值指令去掉。
圖片
得到匯編指令后編譯器會最終將其轉(zhuǎn)為CPU可以認知的二進制機器指令,每個源文件被編譯后都會生成一個目標文件,目標文件中就是轉(zhuǎn)換后的二進制機器指令。
圖片
最后,鏈接器會把目標文件打包成最終的可執(zhí)行程序,