編譯程式即是將高階語言書寫的源程式翻譯成與之等價的目標程式(組合語言或機器語言)。其工作可分為六個階段,見下圖:
對於編譯的各個階段,邏輯上可以劃分為前端和後端兩部分。前端包括詞法分析到中間**生成中各個階段的工作,後端則是優化及目標**生成的階段。
以中間**為分水嶺的原因是把編譯過程分解為與機器有關和無關兩個部分。這樣對同一種程式語言只需要乙個前端,只要針對不同的機器開發不同的後端即可,前後端結合形成編譯器。當語言改動時也只涉及前端部分的維護。而對於不同的程式語言只要有不同的前端,然後與同乙個後端結合就可以得到程式語言在某種機器上的編譯器了。
下面簡單介紹一下每個步驟(模組)的目的和工作,注意,下文步驟中使用的x,y,z等符號,在詞法分析後實際上會是乙個內部識別符號,之所以照用原文只是為了方便理解。
源程式可以看做是多行的字串,詞法分析是編譯過程的第一階段,其任務是對源程式逐字元掃瞄,從中識別出乙個個「單詞」,「單詞」又叫做符號,它是程式語言的基本語法單位,如關鍵字(保留字)、識別符號、常數、運算子、分隔符等。
詞法分析程式輸出的「單詞」以二元組的形式輸出,即其種類和值。詞法分析過程依據語言的詞法規則,即描述「單詞」結構的規則。
如:pascal源程式一則:
var x,y,z:real;
x:= y+z*60;
詞法分析階段將語句分割成以下單詞序列:型別值
型別值1. 關鍵字
var9. 分號
;2. 識別符號
x10. 識別符號
x3. 逗號
,11. 賦值號
:=4. 識別符號
y12. 識別符號
y5. 逗號
,13. 加號
+6. 識別符號
z14. 識別符號
z7. 冒號
:15. 乘號
*8. 標準識別符號
real
16. 整常數
6017. 分號
;在詞法分析結果的基礎上,語法分析是根據語言的規則將單詞符號串行分解成語法單位,如「表示式」、「語句」、」程式「等。語法規則就是語法單位的構成規則,通過語法分析確定整個輸入串能否構成乙個語法上正確的程式。如果程式沒有錯誤,語法分析後就能正確的構造出語法樹;否則就會指出語法錯誤,並給出診斷。
詞法分析和語法分析本質上都是在對源程式的結構進行分析。
如:根據上面詞法分析的結果可以構造出的語法樹如下圖:
語義分析階段主要分析語法結構的含義,檢查源程式是否包含靜態語義錯誤,並收集型別資訊供**生成階段使用。只有語法和語義都正確的源程式才能翻譯成正確的目標程式。
語義分析的主要工作就是對型別進行分析和檢查,一般型別檢查包括兩點:型別載體及在其上的運算。如,整除取餘運算子只能對整數使用,如果運算物件是浮點數就認為是型別錯誤。
在確定源程式語法和語義後,就可以對其進行翻譯並給出源程式的內部表示。對於宣告語句,要記錄所遇到的符號資訊,此階段還負責填寫校驗符號表,對於可執行語句則分析其結構合理性,並補充必要的步驟。
如:對於上面的變數宣告語句var x,y,z:real;
應生成下面的符號表(real型別佔4位)
符號型別
邏輯位址
xreal0y
real4z
real
8對於上面變數賦值語句x:= y+z*60;
生成的語法樹經過語義分析後應如下圖,其中增加了乙個語義處理點 inttoreal,它的作用是將整型數轉換為浮點數:
該階段是根據語義分析的結果生成中間**,「中間」即意味著並非可執行的機器碼,它是一種簡單含義明確的記號系統,有若干種形式,但所有中間**的共同特徵均為與具體機器無關,類似於演算法偽**的作用。最常用的中間**是一種與彙編高度類似的三位址碼,採用四元式實現,其形式為:(運算子, 運算物件1, 運算物件2, 運算結果)。
如:對於上述提到的賦值語句x:= y+z*60;
可根據語義分析的結果生成以下四元序列式:
(inttoreal, 60, -, t1)
(*, z, t1, t2)
(+, y, t2, t3)
(:=, t3, -, x)
其中t1, t2, t3均為編譯程式生成的臨時變數,用於存放臨時的結果。
由於編譯器將源程式翻譯成中間**的工作是按固定模式進行的,因此可以發現中間**中往往在時間和空間上均有較大浪費。當要生成高效的目標**則必須進行優化,優化可以在中間**生成階段進行,也可以在目標**生成階段執行。
由於中間**與具體機器無關,因此對中間**的優化主要是對程式的控制流和資料流的分析之上。
如:對上述賦值語句x:= y+z*60;
生成的四元序列式優化,可以發現60是已知的常數,將它轉換為浮點數60.0也可以在編譯時完成,沒有必要生成乙個四元式。同時,發現t3的作用只是將結果傳遞給x,同樣也不需要生成乙個四元式。因此可優化成如下等價四元序列式:
(*, z, 60.0, t1)
(+, y, t1, x)
當然這只是很簡單的優化,實際上的優化要複雜的多,會涉及公共子表示式的提取等更多技術。
目標**生成是編譯器的最後乙個階段,這一階段將中間**轉化為目標機器上的絕對指令**、可重定位指令**或彙編指令**,這一階段與具體機器密切相關。
如:使用兩個暫存器r1和r2將上述的四元序列式生成下面的目標**:
movf z, r2
mulf #60.0, r2
movf y, r1
addf r2, r1
movf r1, x
符號表的主要作用就是記錄符號的資訊,以輔助語義檢查和**生成。在編譯過程中需要對符號表進行快速的查詢插入、修改、刪除等操作。
符號表的建立一般始於詞法分析階段,但也可以始於詞法分析和語義分析階段。有時候符號表的使用還會伴隨到目標**的執行階段。
源程式不可避免的會出現錯誤,這些錯誤大致分為靜態錯誤和動態錯誤。
動態錯誤又稱為動態語義錯誤,它們發生在程式執行時,例如變數為0時做除數、引用陣列元素下標錯誤等。
靜態錯誤是編譯階段發現的程式錯誤,可分為語法錯誤和靜態語義錯誤,如關鍵字拼寫錯誤、標點符號錯誤、表示式缺少運算元、括號不匹配等問題就是語法錯誤,語義錯誤主要指語義分析階段發現的運算子與運算物件型別不合法等錯誤。
在編譯發生錯誤時,編譯過程應想辦法能夠繞過去,以便在一次編譯中發現盡可能多的錯誤。
編譯原理概述
一 編譯過程分析 編譯軟體讀取源程式 字元流 對之進行詞法和語法的分析,將高階語言指令轉換為功能等效的彙編 再由匯程式設計序轉換為機器語言,並按照作業系統對可執行檔案格式的要求鏈結生成可執行程式.二 編譯流程表 c源程式 c檔案 編輯器 預處理過程 c檔案 編譯 優化過程 s或.asm檔案 編譯器 ...
編譯原理概述
計算機不能直接理解 高階語言 只能直接理解 機器語言 所以必須要把高階語言 翻譯 成機器語言,計算機才能執行高階語言編寫的程式。我們可以粗略地把程式語言分為兩類 編譯型語言 和解釋型語言 常用的c c pascal和最近流行的go語言都是編譯型語言,而python ruby等則是解釋型語言,解釋型語...
編譯原理概述
編譯程式的工作過程 編譯程式的結構 編譯器的自舉和移植 高階語言轉換為可執行語言的過程 以gcc編譯器為例 原始檔為hello.c 步驟一 預處理。將原始檔hello.c預處理成hello.i。該步是將 include中包含的標頭檔案匯入到原始檔中。命令是gcc e hello.c 步驟二 編譯。將...