第一講:緒論
特別聲明:以下內容,源自 大學慕課 《編譯原理》哈爾濱工業大學 陳鄞,文章經個人整理所得,僅供學習交流
(一) 什麼是編譯
(1) 基本概念
先說幾個必備的概念
A:機器語言
機器語言是機器能直接識別的程序語言或指令代碼,勿需經過翻譯,每一操作碼在計算機內部都有相應的電路來完成它,或指不經翻譯即可爲機器直接理解和接受的程序語言或指令代碼。機器語言使用絕對地址和絕對操作碼。不同的計算機都有各自的機器語言,即指令系統。從使用的角度看,機器語言是最低級的語言。
—— 百度百科
個人理解:
-
從表面形式來看,機器語言就是一堆1和0組成的代碼,也就是用二進制代碼表達指令,但更確切一點來說,機器語言是由高低電位構成的,指定高電位爲1,低電位爲0,而我們對電路進行一定的設計後,電路中高低電位的輸入輸出正好與2進制狀態相符,所以我們也就看到了 1、0的那種表現形式
-
計算機能直接理解機器語言,不需要經過任何處理(因爲1、0 和其實體電路結構是相關的)
-
如下圖中 C706 0000 0002(16進制)C706 代表操作碼,0000 0002 代表操作數 代表賦值語句 X = 2
- 補充:爲了簡化二進制,照顧人的易讀性所以用十六進制來表示(09和af),機器可不能直接識別十六進制數,計算機內部的一切信息的存取以及傳輸還都是以二進制形式進行的
疑問:實際情況下,我們直接用二進制進行描述一些程序等是非常麻煩的,那爲什麼不直接轉換成容易理解的十進制呢?然後運行的時候再轉爲二進制呢?而出現了八進制或者十六進制這樣的概念
答案:首先直接使用二進制當然是比較麻煩的,枯燥,且很長很長,所以轉換成一些更高的進制,就可以大幅度縮小長度,而十進制描述雖然符合人的行爲習慣,容易被人接受,但是直接與計算機結構關聯卻有一些不太合適,而八進制或者十六進制分別是 2^3 以及 2^4 這一點使得,進制之間的轉換會比較容易,同時8位二進制數爲一個字節,而兩位十六進制剛好可以表示一個字節,例如,F1 對應二進制爲 11110001,同樣可以看到,每一位十六進制數,也轉換成了四位二進制數
B:彙編語言
彙編語言(assembly language)是一種用於電子計算機、微處理器、微控制器或其他可編程器件的低級語言,亦稱爲符號語言。在彙編語言中,用助記符代替機器指令的操作碼,用地址符號或標號代替指令或操作數的地址。在不同的設備中,彙編語言對應着不同的機器語言指令集,通過彙編過程轉換成機器指令。特定的彙編語言和特定的機器語言指令集是一一對應的,不同平臺之間不可直接移植。 [1]
—— 百度百科
簡單概括:低級,不具有移植性,能直接訪問計算機硬件,效率高,佔用資源少,同時使用助記符(Memoni)代替操作碼,用地址符號(Symbol)或標號(Label)代替地址碼。如上圖的MOV X,2 同樣代表賦值語句 X = 2
C:高級語言
高級編程語言(High-level programming language)是高度封裝了的編程語言,與低級語言相對。它是以人類的日常語言爲基礎的一種編程語言,使用一般人易於接受的文字來表示,有較高的可讀性,以方便對電腦認知較淺的人亦可以大概明白其內容。
—— 維基百科
這沒什麼好說的,就日常編程所做的,x = 2
Em 鋪墊好像是長了點哈
從上圖可知,內容經過編譯這個過程以後,從高級轉換到低級的形式(從人樂意看的內容轉換成機器樂意看的內容)
編譯的定義:將高級語言(源語言)翻譯成彙編語言或機器語言(目標語言)的過程
(2) 編譯器在語言處理系統中的位置
前面我們說了編譯的一個基本概念,而爲了建立可執行的目標程序,除了編譯器外,我們還需要一些其他的程序進行配合,下圖就是一個語言處理的基本過程,注意留意編譯器所處的位置
簡單介紹一下流程中的內容
A:預處理器(Preprocessor)
-
一個源程序可能分成幾個模塊存放在不同的文件裏,將這些源程序彙集在一起的任務,這時候就需要預處理器把存儲在不同文件中的源程序聚合在一起
-
把稱爲宏的縮寫語句轉換爲原始語句
B:編譯器(Compiler)
- 將高級語言翻譯成彙編語言或機器語言
C:彙編器(Assembler)
- 將彙編語言翻譯成可重定位的機器語言
- 若在編譯器階段已經直接將高級語言翻譯成機器語言,則可以省略彙編器
- 可重定位(Relocatable)/ 可再裝配:數據在內存存放的起始位置 L 不是固定的,起始位置 + 相對地址 = 絕對地址
D:加載器(Loader)
- 修改可重定位地址
- 將修改後的指令和數據放到內存中適當的位置
E:鏈接器(Linker)
- 將多個可重定位的機器代碼文件(包括庫文件)連接到一起
- 解決外部內存地址問題
(二) 編譯系統結構
(1) 結構概述
A:前端(fornt end)
與源語言相關,字符流——詞法分析器——詞法單元流——語法分析器——語法樹——語義分析器——語法樹——中間代碼生成器
B:後端(back end)
與目標語言相關,中間表示形式 ——機器無關代碼優化器——中間表示形式——目標代碼生成器——目標機器語言——機器相關代碼優化器——目標機器語言
上述字體加粗的爲後端部分
(三) 詞法分析概述
(1) 基本概念
從左向右逐行掃描源程序的字符,識別出各個單詞,確定單詞的類型,將識別出的單詞轉換成統一的機內表示——此法單元(token)形式
token:<種別碼,屬性值>
單詞類型 | 種別 | 種別碼 | |
---|---|---|---|
1 | 關鍵字 | program、is、else、then、… | 一詞一碼 |
2 | 標識符 | 變量名、數組名、記錄名、過程名、… | 多詞一碼 |
3 | 常量 | 整型、浮點型、字符型、布爾型、… | 一型一碼 |
4 | 運算符 | 算數(+ - * 、 ++ --)關係(> < == != >= <=)邏輯(&|~) | 一詞一碼或一型一碼 |
5 | 界限符 | ; () = {} … | 一詞一碼 |
(2) 例題一
種別碼本身應該是一個整數,爲了上例中爲了直觀,使用了宏定義的形式
-
WHILE:表示 while ,關鍵字,一詞一碼
-
IDN(identify):表示標識符,第一個分量全爲IDN,第二個分量爲其字面值以互相區分
-
NE(not equal):表示不等,運算符,一詞一碼或一型一碼
-
CONST:表示常量,一型一碼
-
SLP、SRP、LP、RP:分別表示左右小括號以及左右花括號,界限符, 一詞一碼
-
INC:表示 自增 ++ 運算符,一詞一碼或一型一碼
-
SEMI:表示 分號 ;界限符,一詞一碼
(四) 語法分析概述
(1) 基本概念
語法分析器(parser)從詞法分析器輸出的token序列中識別出各類短語,並依據這些規則所體現出的語言構造的層次性,用各記號的第一元建成一種樹形的中間表示
(2) 例題一:賦值語句的分析樹
從下往上看,一個標識符 rete * 一個數字60 組成了一個新的表達式,而它又 + 另一個標識符 initial 組成了一個更大的表達式,接着通過 = 與標識符 postion 組成了最終的賦值語句
(3) 例題二:變量聲明語句的分析樹
D:declaration(聲明)
T:type(類型 )
IDS:identifiers sequence(標識符序列)
文法在圖片中有提到,即 ① 聲明 = 類型 + 標識符序列 + ; ② 類型 = int 或 real 或 char 或bool ③ 一個標識符 id 本身 可以構成一個標識符序列,一個標識符序列 + , + 標識符 id 可以構成一個更大的標識符序列
這樣一看這個圖就很直觀了
(五) 語義分析概述
(1) 收集標識符的屬性信息
A:種屬(Kind)
簡單變量、符合變量(數組、記錄 …)、過程、…
B:類型(Type)
整型、實型、字符型、布爾型、指針型、…
C:存儲位置、長度
一個例子就明白了:
例如,創建一個實型數組x ,所以其相對地址爲 0 ,其含有 8個元素,同時假設一個實型變量佔用 8 個字節,這個數組佔據了0-63的地址 ,所以下一個 整型變量 i 只能從 64 開始,而假設一個整型變量佔用 4 個字節,j 就需要從64 + 4,68開始
D:值
E:作用域
F:參數和返回值信息
參數個數、參數類型、參數傳遞方式、返回值類型
總結:符號表
這些收集到的標記符屬性信息,都會被存放到一個叫做符號表的數據結構中,其中有着例如 TYPE、KIND 等多種屬性,同時符號表通常帶有一個字符串表如下圖
NAME = 標識符在字符串表中的起始位置 + 長度
(2) 語義檢查
- 變量或過程未經聲明就使用
- 變量或過程名重複聲明
- 運算分量類型不匹配
- 操作符與操作數之間的類型不匹配
- 數組下標不是整數
- 對非數組變量使用數組訪問操作符
- 對非過程名使用過程調用操作符
- 過程調用的參數類型或數目不匹配
- 函數返回類型有誤
(六) 中間代碼生成
中間代碼生成:經過語法分析和語義分析後,許多編譯器爲源程序產生更低級的顯示中間表示,可以理解爲一種抽象的程序
(1) 常用的中間表示形式
三地址碼 (Three-address Code) (在這裏進行簡單介紹)
- 三地址碼由類似於彙編語言的指令序列組成,
- 每個指令最多有三個操作數(operand)
語法結構樹/語法樹 (Syntax Trees)(後面詳細講,這裏不涉及)
(2) 常用的三地址指令
-
x = y op z :op 是一個二元運算符,y 和 z 是兩個運算分量的地址,x 是運算結果的存放地址
-
x = op y:op 在這裏是一個一元運算符,因此只有兩個操作數 x y
-
x = y :也只有兩個操作數
-
if x relop y goto n :如果 x 和 y 滿足 relop 關係,就跳轉到 n 對應的指令
-
goto n:直接跳轉到 n 對應的指令
-
param x:將 x 設置爲參數
-
call p,n:p 是過程的名字,n 是過程的個數
-
return:跳轉到地址 x 對應的指令
-
x = y[i]:y 表示數組的名字,即基地址,i 是數組元素的偏移地址,不是下標
-
x[i] = y:將一個變量的值賦值給數組元素
-
最後三個:與指針相關的指令
(3) 三地址指令的表示
-
四元式 (Quadruples)(下面提一下這個)
- (op, y, z, x)
-
三元式 (Triples)
-
間接三元式 (Indirect triples)
(4) 中間代碼生成的例子
說明一下,右邊,冒號左邊的數字代表指令的編號,例子中從100取到112,j 代表 jump
如何看這個程序做了什麼呢,例如第一行:100:(j<,a,b,102) 就是說,當 a < b 的時候 跳轉到 102 指令,若不滿足就繼續執行 101 指令,找這種方式,對應着代碼看,這個例子是非常直觀的
(七) 目標代碼生成
-
目標代碼生成以源程序的中間表示形式作爲輸入,並把它映射到目標語言
-
目標代碼生成的一個重要任務是爲程序中使用的變量合理分配寄存器
(八) 機器無關/有關優化器
代碼優化
- 爲改進代碼所進行的等價程序變換,使其運行得更快一些、佔用空間更少一些,或者二者兼顧
(九) 整點題目練練手
① 編譯是對( ) 【正確答案:C】
-
A:機器語言的執行
-
B:彙編語言的翻譯
-
C:高級語言的翻譯
-
D:高級語言程序的解釋執行
② 用高級語言編寫的程序經編譯後產生的程序叫( ) 【正確答案:B】
- A:源程序
- B:目標程序
- C:連接程序
- D:解釋程序
③ ( )不是編譯程序的組成部分 【正確答案:C】
- A:詞法分析程序
- B:代碼生成程序
- C:設備管理程序
- D:語法分析程序
④ 源程序是句子的集合,( )可以較好地反映句子的結構 【正確答案:B】
- A:線性表
- B:樹
- C:完全圖
- D:堆棧
⑤ 編譯程序是一種( ) 【正確答案:B】
- A:彙編程序
- B:翻譯程序
- C:解釋程序
- D:目標程序
⑥ 按邏輯上劃分,編譯程序第三步工作是( ) 【正確答案:A】
- A:語義分析
- B:詞法分析
- C:語法分析
- D:代碼生成
⑦ 編譯程序中語法分析器接收以( )爲單位的輸入 【正確答案:A】
- A:單詞
- B:表達式
- C:產生式
- D:句子
⑧ 編譯過程中,語法分析器的任務就是( ) 【正確答案:B】
- A:分析單詞是怎樣構成的
- B:分析單詞串是如何構成語句和聲明的
- C:分析語句和聲明是如何構成程序的
- D:分析程序的結構
⑨ 語法分析時所依據的是( ) 【正確答案:A】
- A:語法規則
- B:詞法規則
- C:語義規則
- D:等價變換規則
總結
緒論部分的知識比較少,主要是對編譯原理的基本知識進行了一定的總結概括,以及一些基本知識的入門,讓我們對編譯有一個初步的概念,更加詳細的課程在後面的章節,後面再更新,最近在忙着寫一些東西,可能更新文章會慢一些,希望大家見諒,再次感謝大家的支持,謝謝!!!
結尾
如果文章中有什麼不足,歡迎大家留言交流,感謝朋友們的支持!
如果能幫到你的話,那就來關注我吧!如果您更喜歡微信文章的閱讀方式,可以關注我的公衆號
在這裏的我們素不相識,卻都在爲了自己的夢而努力 ❤
一個堅持推送原創開發技術文章的公衆號:理想二旬不止