習(xí):從詞法分析到代碼生成的完整知識重構(gòu))
1. 項目概述為什么“期末總復(fù)習(xí)”是編譯原理學(xué)習(xí)的關(guān)鍵一躍又到了學(xué)期末看著桌上那本厚厚的《編譯原理》教材和一堆關(guān)于詞法分析、語法分析、語義分析的筆記是不是感覺頭大很多同學(xué)把編譯原理視為計算機專業(yè)“最難啃的骨頭”之一知識點抽象、概念環(huán)環(huán)相扣平時學(xué)起來就吃力期末復(fù)習(xí)更是無從下手。網(wǎng)上搜索“編譯原理 期末”關(guān)聯(lián)出來的往往是“java編譯原理”、“如何構(gòu)造預(yù)測分析表”、“預(yù)測分析表是如何得出來的”這類非常具體且核心的問題這恰恰說明了大家的痛點知道它重要但不知道如何把零散的知識點串聯(lián)成一個能解題、能理解的系統(tǒng)。我經(jīng)歷過這個階段也帶過不少學(xué)弟學(xué)妹度過編譯原理的考試周。我發(fā)現(xiàn)最大的誤區(qū)就是把期末復(fù)習(xí)等同于“再看一遍書”或“狂刷課后題”。編譯原理的復(fù)習(xí)本質(zhì)上是一次“知識重構(gòu)”。你需要從一個更高的視角把前端詞法、語法、語義分析、中端中間代碼優(yōu)化和后端目標(biāo)代碼生成的零散模塊像搭積木一樣按照“源代碼 - 目標(biāo)代碼”的完整流水線重新組裝起來。這次“總復(fù)習(xí)”項目目的就是幫你完成這次重構(gòu)。它不是簡單的知識點羅列而是帶你走一遍編譯器設(shè)計師的思考路徑讓你明白每個階段“為什么”要這么做以及“如何”從上一個階段推導(dǎo)到下一個階段。最終目標(biāo)不僅是應(yīng)對考試更是讓你真正建立起對程序如何從文本變成可執(zhí)行文件這一過程的系統(tǒng)性認(rèn)知。2. 復(fù)習(xí)核心框架構(gòu)建你的“編譯器”思維模型2.1 從宏觀視角理解編譯的六個階段編譯原理教材通常將編譯過程劃分為六個階段詞法分析、語法分析、語義分析、中間代碼生成、代碼優(yōu)化和目標(biāo)代碼生成。期末復(fù)習(xí)時死記硬背這六個名字沒有意義。你需要建立的是一個動態(tài)的、數(shù)據(jù)流驅(qū)動的思維模型。想象你正在編寫一個最簡單的編譯器處理一段類似a b c * 2的賦值語句。你的“編譯器”大腦應(yīng)該這樣工作詞法分析掃描器你的眼睛就是掃描器。它讀入字符流“a”, “”, “b”, “”, “c”, “”, “2”識別出哪些是獨立的單詞詞素。這里“a”、“b”、“c”是標(biāo)識符ID“”是賦值號ASSIGN“”、“”是運算符OP“2”是整數(shù)常量NUM。輸出是一串記號Token序列[ID(a), ASSIGN, ID(b), OP(), ID(c), OP(*), NUM(2)]。復(fù)習(xí)關(guān)鍵掌握正規(guī)式、有限自動機NFA、DFA及其等價轉(zhuǎn)換??碱}常給出一段單詞的規(guī)則描述如“標(biāo)識符以字母開頭后接字母數(shù)字”讓你寫出正規(guī)式或畫出DFA。語法分析解析器你的大腦現(xiàn)在開始理解句子結(jié)構(gòu)。它接收Token流根據(jù)預(yù)定義的語法規(guī)則通常是上下文無關(guān)文法構(gòu)建一棵語法分析樹。對于上面的表達(dá)式文法規(guī)則可能是E - E T | T; T - T * F | F; F - ( E ) | id | num。解析器會推導(dǎo)出a b c * 2符合賦值語句 - id E的結(jié)構(gòu)并明確c*2是一個子表達(dá)式T然后再與b相加E。復(fù)習(xí)關(guān)鍵這是重中之重。必須熟練掌握自頂向下LL(1)和自底向上LR兩大類分析方法。尤其是LL(1)文法的判斷、First集和Follow集的計算、預(yù)測分析表的構(gòu)造以及LR(0)、SLR(1)、LR(1)、LALR(1)分析器的構(gòu)造和項目集規(guī)范族。語義分析檢查器現(xiàn)在檢查句子是否“有意義”。語法樹只保證結(jié)構(gòu)正確但a b c * 2是否合法還需要語義規(guī)則。檢查包括變量a, b, c是否已聲明b和c的類型是否支持乘法運算乘法結(jié)果與b的類型是否支持加法加法結(jié)果類型能否賦值給a此階段會收集類型信息裝飾語法樹。復(fù)習(xí)關(guān)鍵掌握屬性文法的概念特別是綜合屬性和繼承屬性如何計算。理解符號表Symbol Table的數(shù)據(jù)結(jié)構(gòu)如哈希表、鏈表和作用記錄標(biāo)識符的名字、類型、作用域、存儲位置等。中間代碼生成翻譯器將經(jīng)過檢查的語法樹翻譯成一種抽象、機器無關(guān)的表示形式常見的有三地址碼如t1 c * 2; t2 b t1; a t2、四元式、逆波蘭式等。中間代碼像是“通用匯編”它抹平了具體語法細(xì)節(jié)為后續(xù)優(yōu)化和生成不同目標(biāo)機的代碼做準(zhǔn)備。復(fù)習(xí)關(guān)鍵掌握如何將常見的程序結(jié)構(gòu)賦值、算術(shù)運算、控制流if/while、數(shù)組訪問等翻譯成三地址碼。這是連接前端理論和后端實踐的重要橋梁。代碼優(yōu)化優(yōu)化器對中間代碼進(jìn)行等價變換以提高運行時效率或減少代碼大小。例如對于t1 c * 2;如果編譯器知道2是2的冪可能會優(yōu)化為t1 c 1;左移一位代替乘法。優(yōu)化可以在中間代碼層面進(jìn)行也可以在目標(biāo)代碼層面進(jìn)行。復(fù)習(xí)關(guān)鍵掌握常見的局部優(yōu)化如常量傳播、公共子表達(dá)式消除、死代碼刪除和循環(huán)優(yōu)化如代碼外提、強度削弱的基本思想和方法。能識別出給定代碼片段可進(jìn)行的優(yōu)化。目標(biāo)代碼生成匯編器將優(yōu)化后的中間代碼映射到特定目標(biāo)機器如x86、ARM的指令集和寄存器上生成最終的匯編或機器碼。這涉及到寄存器分配哪個變量放哪個寄存器、指令選擇用哪條機器指令實現(xiàn)一個操作、棧幀管理函數(shù)調(diào)用時如何分配局部變量等。復(fù)習(xí)關(guān)鍵了解基本塊、流圖的概念掌握簡單的寄存器分配策略如寄存器描述符和地址描述符理解如何為三地址碼生成目標(biāo)代碼的框架。注意這六個階段并非總是嚴(yán)格串行現(xiàn)代編譯器常采用多遍掃描且階段間有交叉。但復(fù)習(xí)時先建立清晰的階段劃分模型是理解一切的基礎(chǔ)。2.2 核心數(shù)據(jù)結(jié)構(gòu)符號表與語法樹貫穿整個編譯過程有兩個核心數(shù)據(jù)結(jié)構(gòu)像“中樞神經(jīng)”一樣重要符號表從語義分析階段開始建立并在后續(xù)階段不斷查詢和更新。它記錄了所有標(biāo)識符變量、函數(shù)、常量等的屬性。復(fù)習(xí)時要掌握符號表的常見操作插入、查找、刪除、作用域的實現(xiàn)如通過棧式符號表管理嵌套作用域、以及如何處理同名標(biāo)識符在不同作用域的問題。語法樹/抽象語法樹AST語法分析產(chǎn)生分析樹但通常會被進(jìn)一步簡化為AST去掉那些對語義無關(guān)緊要的語法細(xì)節(jié)如分隔符、部分非終結(jié)符。AST是后續(xù)語義分析、中間代碼生成所依賴的核心中間表示。要理解AST與分析樹的區(qū)別并能根據(jù)文法畫出對應(yīng)代碼段的AST。3. 重難點深度剖析與解題心法3.1 語法分析預(yù)測分析表LL(1)表的構(gòu)造——從First集與Follow集說起這是期末考和考研中的絕對高頻考點。問題“編譯原理中的預(yù)測分析表是如何得出來的”直擊核心。很多同學(xué)能背步驟但不懂原理題目稍一變就出錯。我們來徹底搞懂它。核心思想LL(1)分析器在決定用哪個產(chǎn)生式展開當(dāng)前非終結(jié)符時只向前看一個輸入符號Lookahead。預(yù)測分析表M[A, a]就是一個指南告訴分析器“當(dāng)棧頂是非終結(jié)符A且當(dāng)前輸入符號是a時應(yīng)該選用哪個產(chǎn)生式或者報錯?!睒?gòu)造步驟與內(nèi)在邏輯計算每個文法符號X的First集First(X)表示由X推導(dǎo)出的串的可能開頭終結(jié)符集合。規(guī)則若X是終結(jié)符First(X) {X}。若X是非終結(jié)符且存在產(chǎn)生式X - Y1 Y2 ... Yk。將First(Y1)中所有非ε的元素加入First(X)。如果First(Y1)包含ε則繼續(xù)查看First(Y2)將其非ε元素加入以此類推。如果所有Yi都能推出ε則將ε加入First(X)。為什么因為分析器要預(yù)測它必須知道A展開后產(chǎn)生的字符串第一個可能是什么終結(jié)符。計算每個非終結(jié)符A的Follow集Follow(A)表示在某些句型中緊跟在A后面的終結(jié)符集合。規(guī)則將結(jié)束符$加入Follow(開始符號)。若有產(chǎn)生式B - α A β則將First(β)中所有非ε的元素加入Follow(A)。若有產(chǎn)生式B - α A或B - α A β且β能推出ε即ε ∈ First(β)則將Follow(B)加入Follow(A)。為什么考慮A可能推出空串ε的情況。如果A推出了ε那么決定下一步動作的就是原本跟在A后面的那個符號即Follow(A)中的符號。所以Follow集是為了處理“空”產(chǎn)生式。構(gòu)造預(yù)測分析表M對文法G的每個產(chǎn)生式A - α執(zhí)行以下兩步對于First(α)中的每個終結(jié)符a將A - α填入M[A, a]。這很直觀當(dāng)前輸入符a正好是α能推導(dǎo)出的串的開頭。如果ε ∈ First(α)那么對于Follow(A)中的每個終結(jié)符b包括$也將A - α填入M[A, b]。這就是關(guān)鍵因為α能推出空意味著A可能“消失”那么該用什么產(chǎn)生式就由跟在A后面的符號Follow(A)來決定。如果同一表項被填入了多個產(chǎn)生式則該文法不是LL(1)文法。實戰(zhàn)例題解析 給定文法E - T E E - T E | ε T - F T T - * F T | ε F - ( E ) | id計算First和Follow集過程略這是基本功First(F) { (, id };First(T) { *, ε };First(T) { (, id };First(E) { , ε };First(E) { (, id }Follow(E) { $, ) };Follow(E) { $, ) };Follow(T) { , $, ) };Follow(T) { , $, ) };Follow(F) { *, , $, ) }構(gòu)造預(yù)測分析表非終結(jié)符id*()$EE - T EE - T EEE - T EE - εE - εTT - F TT - F TTT - εT - * F TT - εT - εFF - idF - ( E )以E - ε為例因為First(ε) {ε}所以看Follow(E) { $, ) }。因此在E行)列和$列填入E - ε。這意味著當(dāng)棧頂是E輸入是)或$句子結(jié)束時應(yīng)用ε產(chǎn)生式將E彈出棧因為它匹配了“空”輸入。以T - * F T為例First(* F T) {*}所以只在T行*列填入該產(chǎn)生式。避坑指南計算Follow集時最容易漏規(guī)則“B - α A β且β能推出ε則Follow(B)加入Follow(A)”。很多同學(xué)只記得B - α A的情況。表項沖突如果同一個格子要填兩個不同的產(chǎn)生式說明文法不是LL(1)。常見原因有左遞歸、公共左因子。復(fù)習(xí)時要掌握消除左遞歸和提取左因子的方法。ε產(chǎn)生式的填寫務(wù)必記住只有當(dāng)一個產(chǎn)生式的First集包含ε時才去查找對應(yīng)非終結(jié)符的Follow集來填表。3.2 語法分析LR分析器家族LR(0), SLR(1), LR(1), LALR(1)的辨析與構(gòu)造這是另一個難點尤其是項目集規(guī)范族LR(0)項集族的構(gòu)造。核心是理解“狀態(tài)”的概念。核心思想LR分析器維護(hù)一個狀態(tài)棧而LL是符號棧狀態(tài)代表了分析器在解析過程中所處的“位置”即已經(jīng)看到了產(chǎn)生式的多少部分。通過查詢 ACTION/GOTO 表來決定是移進(jìn)、規(guī)約、接受還是報錯。構(gòu)造流程與關(guān)鍵區(qū)別LR(0)項一個產(chǎn)生式加上一個“·”點表示已識別和未識別的分界如A - α·β。構(gòu)造LR(0)項集規(guī)范族CLOSURE操作如果項A - α·Bβ在集合I中且B是非終結(jié)符那么對于B的所有產(chǎn)生式B - γ將B - ·γ加入I。這表示我們期待接下來看到B推導(dǎo)出的東西。GOTO操作GOTO(I, X)是從項集I出發(fā)所有形如A - αX·β的項的集合即點越過符號X。從初始項集CLOSURE({S - ·S})開始反復(fù)應(yīng)用GOTO操作直到不再產(chǎn)生新項集。每個項集就是一個狀態(tài)。從項集到分析表以SLR(1)為例這是考試中最??嫉腁CTION表針對終結(jié)符。移進(jìn)如果項A - α·aβ在狀態(tài)i中且GOTO(i, a) j則ACTION[i, a] sj移進(jìn)a并跳轉(zhuǎn)到狀態(tài)j。規(guī)約如果項A - α·在狀態(tài)i中點在最后表示一個完整產(chǎn)生式已識別則對于Follow(A)中的所有終結(jié)符aACTION[i, a] rk用第k個產(chǎn)生式A - α規(guī)約。這就是SLR(1)它用Follow集來限制規(guī)約范圍。接受如果項S - S·在狀態(tài)i中則ACTION[i, $] acc。GOTO表針對非終結(jié)符。如果GOTO(i, X) j則GOTO[i, X] j。LR家族辨析LR(0)最弱。只要狀態(tài)中有規(guī)約項A - α·就對所有輸入符號都執(zhí)行規(guī)約動作。這必然會產(chǎn)生大量“移進(jìn)-規(guī)約”沖突。實際很少直接用。SLR(1)在LR(0)的基礎(chǔ)上用Follow(A)限制了規(guī)約項A - α·的適用范圍。只有當(dāng)當(dāng)前輸入符a在Follow(A)中時才執(zhí)行規(guī)約。解決了LR(0)的大部分沖突能力較強且構(gòu)造簡單是考試重點。LR(1)能力最強。它將“向前看符號”直接集成到項中形成LR(1)項[A - α·β, a]。規(guī)約項[A - α·, a]只對特定的向前看符號a進(jìn)行規(guī)約。狀態(tài)數(shù)可能比SLR(1)多很多。LALR(1)在LR(1)的基礎(chǔ)上合并那些核心項點位置相同相同、僅向前看符號集不同的狀態(tài)。狀態(tài)數(shù)與SLR(1)相同分析能力介于SLR(1)和LR(1)之間是實踐中如Yacc/Bison最常用的。解題心法畫狀態(tài)圖構(gòu)造項集規(guī)范族時建議在草稿紙上畫出狀態(tài)轉(zhuǎn)換圖。每個狀態(tài)項集是一個節(jié)點用符號X終結(jié)符或非終結(jié)符做邊連接到下一個狀態(tài)GOTO(I, X)。這能極大幫助你理解狀態(tài)機的走向。沖突判斷SLR(1)表中如果同一個ACTION[i, a]格子既有移進(jìn)動作sj又有規(guī)約動作rk這就是“移進(jìn)-規(guī)約沖突”。如果同一個格子有兩個不同的規(guī)約動作是“規(guī)約-規(guī)約沖突”。出現(xiàn)沖突意味著該文法不是SLR(1)文法。牢記步驟先拓廣文法增加S - S- 構(gòu)造LR(0)項集規(guī)范族 - 根據(jù)每個狀態(tài)中的項和Follow集填寫SLR(1)分析表。按部就班不要跳步。3.3 語法制導(dǎo)翻譯與中間代碼生成這部分考查將語法結(jié)構(gòu)翻譯成三地址碼的能力。核心是理解“屬性”如何在語法樹中傳遞和計算。關(guān)鍵考點S-屬性文法與L-屬性文法S-屬性文法只使用綜合屬性屬性值自底向上計算從子節(jié)點傳到父節(jié)點。LR分析器可以方便地實現(xiàn)。L-屬性文法屬性計算可以是深度優(yōu)先從左到右的順序。包含了S-屬性文法。LL分析器可以方便地實現(xiàn)??荚囍谐=o出一段帶有語義規(guī)則的文法讓你判斷是S-屬性還是L-屬性并說明如何計算。翻譯模式與三地址碼生成你需要為每個語法規(guī)則配上一個或多個語義動作用花括號{}括起。例如為表達(dá)式E - E1 T生成三地址碼E - E1 T { E.place new_temp(); // 為E的結(jié)果分配一個臨時變量 emit(E.place “ ” E1.place “ ” T.place); // 生成三地址指令 }控制流語句if, while的翻譯是重點和難點需要用到回填backpatching技術(shù)來管理跳轉(zhuǎn)目標(biāo)的標(biāo)號。回填的核心在生成條件跳轉(zhuǎn)指令時跳轉(zhuǎn)目標(biāo)標(biāo)號可能還不知道。此時先生成一個不完整的指令目標(biāo)地址留空并將這條指令的地址存入一個列表。當(dāng)后續(xù)知道目標(biāo)標(biāo)號時再“回填”到這個列表中的所有指令地址上。實戰(zhàn)示例While語句的翻譯考慮文法S - while ( E ) S1為其設(shè)計翻譯模式生成三地址碼。S - while ( E ) S1 { S.begin new_label(); // 循環(huán)開始標(biāo)號 E.true new_label(); // E為真時跳往的標(biāo)號即S1的入口 E.false S.next; // E為假時跳往的標(biāo)號即循環(huán)后的語句 S1.next S.begin; // S1執(zhí)行完后跳回循環(huán)開始 emit(S.begin “:”); // 生成標(biāo)號 gen_code_for(E); // 生成E的代碼其跳轉(zhuǎn)目標(biāo)由E.true和E.false決定 emit(E.true “:”); gen_code_for(S1); emit(“goto ” S.begin); // S.next 已經(jīng)在E.false中設(shè)置 }這里S.next是一個繼承屬性表示S執(zhí)行完后應(yīng)該跳轉(zhuǎn)到的標(biāo)號由外層環(huán)境提供。new_label()生成一個新的唯一標(biāo)號。gen_code_for是一個遞歸過程為子表達(dá)式或語句生成代碼。4. 期末備考策略與高頻題型實戰(zhàn)4.1 高效復(fù)習(xí)路線圖距離考試可能只剩一兩周時間有限必須高效。第一輪2-3天建立框架攻克核心算法目標(biāo)徹底弄懂NFA到DFA的轉(zhuǎn)化子集構(gòu)造法、First/Follow集計算、LL(1)預(yù)測分析表構(gòu)造、LR(0)/SLR(1)項集規(guī)范族構(gòu)造及分析表生成。這四大塊是試卷大題最可能出現(xiàn)的。方法不看細(xì)節(jié)描述直接找3-5道經(jīng)典例題課本習(xí)題、往年考題對照答案一步步推導(dǎo)。推導(dǎo)過程中把每一步的“為什么”寫在旁邊。自己總結(jié)出算法步驟清單。第二輪2-3天串聯(lián)流程掌握翻譯與優(yōu)化目標(biāo)理解從詞法分析到目標(biāo)代碼生成的完整數(shù)據(jù)流。重點練習(xí)將小程序片段賦值、算術(shù)、if、while翻譯成三地址碼以及在給定的三地址碼序列上進(jìn)行局部優(yōu)化。方法畫出“源代碼 - Token流 - 語法樹 - 三地址碼 - 優(yōu)化后代碼”的完整轉(zhuǎn)換圖。針對每種控制結(jié)構(gòu)默寫其標(biāo)準(zhǔn)的翻譯模板。第三輪1-2天掃蕩概念查漏補缺目標(biāo)復(fù)習(xí)選擇題、填空題可能涉及的所有概念。如編譯各階段任務(wù)、文法的分類Chomsky體系、短語/句柄/素短語、活動記錄、運行時存儲空間組織棧、堆、常見的代碼優(yōu)化技術(shù)名稱等。方法快速瀏覽教材目錄和章節(jié)小結(jié)用自己的話復(fù)述每個概念。制作關(guān)鍵詞閃卡。第四輪考前1天模擬與錯題回顧目標(biāo)找一套往年真題或模擬題嚴(yán)格計時完成。不對答案檢驗時間分配和答題手感。然后重點復(fù)習(xí)之前做錯的題目和易混淆點如LL vs LR SLR vs LR(1)。4.2 高頻題型與答題模板題型一文法與語法分析綜合大題題干形式給一個文法G可能要求1) 判斷文法類型2) 計算First/Follow集3) 判斷是否為LL(1)并構(gòu)造預(yù)測分析表4) 判斷是否為SLR(1)并構(gòu)造分析表5) 分析給定句子。答題模板判斷與預(yù)處理如有左遞歸或公共左因子先進(jìn)行消除/提取。計算First/Follow集在草稿紙上清晰列出所有符號逐步計算。在答卷上整齊呈現(xiàn)最終結(jié)果。構(gòu)造分析表若是LL(1)畫一個矩陣行是非終結(jié)符列是終結(jié)符$。根據(jù)First/Follow集規(guī)則逐一填寫產(chǎn)生式。若是SLR(1)先構(gòu)造LR(0)項集規(guī)范族需畫出所有狀態(tài)I0, I1...然后為每個狀態(tài)i和每個符號填寫ACTION[i, a]和GOTO[i, A]。填寫ACTION規(guī)約項時務(wù)必寫上“根據(jù)Follow(X)”這一依據(jù)。分析句子按照構(gòu)造出的分析表一步步模擬棧和輸入的變化過程。步驟要清晰格式要工整。題型二中間代碼生成大題題干形式給一段小程序包含變量聲明、賦值、算術(shù)運算、if-else、while循環(huán)、數(shù)組訪問等要求生成三地址碼、四元式或逆波蘭式。答題模板聲明處理為所有變量在符號表中分配虛擬地址如offset。逐句翻譯算術(shù)表達(dá)式引入臨時變量自底向上生成t arg1 op arg2。賦值語句x y或x t。數(shù)組訪問a[i]計算地址addr base_a i * width然后通過t *addr取值或*addr t賦值??刂屏鳛閕f和while準(zhǔn)備好標(biāo)號L1, L2...嚴(yán)格按照翻譯模式生成帶條件/無條件跳轉(zhuǎn)的代碼。務(wù)必注意標(biāo)號的管理和跳轉(zhuǎn)目標(biāo)的正確性。題型三代碼優(yōu)化分析/應(yīng)用題題干形式給出一段基本塊的三地址碼要求進(jìn)行優(yōu)化如常量傳播、公共子表達(dá)式消除、死代碼刪除并寫出優(yōu)化后的代碼。答題模板畫出DAG有向無環(huán)圖這是最直觀的方法。為每個變量和運算建立節(jié)點邊表示依賴關(guān)系?;贒AG進(jìn)行優(yōu)化常量折疊如果運算對象是常量直接計算。公共子表達(dá)式如果多個變量指向DAG中同一個運算符節(jié)點則它們是公共子表達(dá)式可以合并。死代碼刪除如果一個變量的值生成后沒有被任何后續(xù)語句引用則生成該值的語句可刪除。從DAG重寫代碼按拓?fù)湫驈腄AG生成新的三地址碼同時復(fù)用臨時變量。4.3 考場實戰(zhàn)技巧與常見失分點時間分配編譯原理考試計算量大。建議拿到試卷先通覽預(yù)估每道題耗時。將至少50%的時間留給最后1-2道語法分析或翻譯的大題。選擇題和概念題不要糾結(jié)快速完成。書寫規(guī)范計算First/Follow集、構(gòu)造分析表時務(wù)必使用清晰的表格保持卷面整潔。狀態(tài)編號I0, I1...、標(biāo)號L1, L2...、臨時變量t1, t2...要前后一致。分步得分即使最終結(jié)果不對計算過程也有分。例如構(gòu)造SLR(1)表時LR(0)項集規(guī)范族畫對了就有可觀的分?jǐn)?shù)。一定要把關(guān)鍵步驟寫在試卷上。常見失分點First/Follow集計算錯誤這是連鎖反應(yīng)的起點一步錯步步錯。務(wù)必復(fù)查特別是ε產(chǎn)生式對First集的影響以及Follow集中“包含$”和“繼承Follow(B)”的規(guī)則。預(yù)測分析表沖突處理遇到?jīng)_突要明確寫出沖突類型和原因如“因First(X) ∩ Follow(A) 非空”而不是簡單地說“不是LL(1)文法”。LR分析表ACTION/GOTO混淆ACTION表對應(yīng)終結(jié)符決定移進(jìn)、規(guī)約、接受GOTO表對應(yīng)非終結(jié)符決定狀態(tài)轉(zhuǎn)移。填表時千萬別搞混列。中間代碼標(biāo)號錯誤if和while翻譯中條件為假時的跳轉(zhuǎn)目標(biāo)S.next最容易弄錯。畫一下控制流圖有助于理清關(guān)系。優(yōu)化過度代碼優(yōu)化時要確保優(yōu)化前后的程序語義等價。特別是涉及指針、函數(shù)調(diào)用副作用時不要輕易做優(yōu)化。編譯原理的復(fù)習(xí)是一場硬仗但也是一次將計算機科學(xué)中最精妙的思想之一——如何讓機器理解人類的意圖——系統(tǒng)化梳理的絕佳機會。當(dāng)你不再視那些枯燥的算法為負(fù)擔(dān)而是將其看作構(gòu)建一個復(fù)雜系統(tǒng)編譯器所必需的精巧工具時理解的門檻就已經(jīng)跨過了一半。最后幾天沉下心來用筆和紙去推導(dǎo)幾個完整的例子比反復(fù)看書更有效。祝你復(fù)習(xí)順利考試成功。