资讯动态

编译原理试卷实战:词法分析到代码生成全链路解析

发布时间:2026/10/9 20:58:43 来源:尧图企业网站定制
简介这份常州工学院编译原理试卷Adoc格式55KB共1个文件面向计算机专业学生及备考编译原理课程的读者用于检验和巩固课程核心知识点。试卷内容覆盖正规表达式与最简DFA构造、逆波兰表示与四元式序列、文法二义性证明与语言描述、First集与Follow集计算、LL(1)文法判定及预测分析表构造以及if-then-else语句的四元式翻译等典型题型基本对应词法分析、语法分析、语义分析与代码生成各阶段。资源以单份doc文档呈现便于打印练习或对照复习适合作为期末备考、课堂测验与自测模拟的参考材料。目前已有391人学习下载可帮助读者熟悉常工院试卷风格与命题重点快速定位薄弱环节并针对性强化训练。1. 从一份编译原理试卷拆解词法到代码生成的完整链路如果你正在准备编译原理的期末复习或者想找一套能覆盖词法分析、语法分析、语义分析和中间代码生成全链路的练习题这份试卷的资源价值在于它把课程里最核心的五个模块压缩进了五道大题。正规表达式与最简DFA、逆波兰表示与四元式、文法二义性证明、LL(1)预测分析表构造、if-then-else的四元式翻译每一道题都对应编译器前端的一个关键阶段。适合两类人一是正在跟这门课死磕的在校生需要一套结构清晰的模拟题来检验自己是否真的理解了算法流程二是已经工作但想回头补编译原理基础的开发者用试卷里的题目当练习比看纯理论教材更容易暴露知识盲区。这份资源不是讲义是一套带明确评分权重的实战题每道题的分值分布本身就告诉你哪些环节是课程重点。2. 正规表达式到最简DFA两道题的完整推导与验证方法2.1 不以0开头但以11结尾的字符串从语言描述到DFA题目要求在字母表{0,1}上构造正规表达式描述所有不以0开头且以11结尾的字符串。先拆解语言约束首字符必须是1末尾两个字符必须是11中间可以是任意01串。正规表达式可以写成1(0|1)*11。这个表达式的结构是第一个1锁定首字符(0|1)*覆盖中间任意长度包括空串最后的11锁定结尾。接下来构造等价的NFA再确定化为DFA。常见做法是先画出NFA状态图状态q0读入1到q1q1读入0或1可以自环q1读入1到q2q2读入1到q3接受状态。确定化时用子集构造法初始状态{q0}读入1到{q1}读入0到空集。从{q1}出发读入0到{q1}读入1到{q1,q2}。从{q1,q2}出发读入0到{q1}读入1到{q1,q2,q3}。从{q1,q2,q3}出发读入0到{q1}读入1到{q1,q2,q3}。接受状态是包含q3的集合。最简化时检查等价状态{q1}和{q1,q2}在输入0时都到{q1}输入1时分别到{q1,q2}和{q1,q2,q3}不等价。{q1,q2,q3}是接受状态单独一组。最终DFA有四个状态转移表如下状态输入0输入1是否接受A死状态B否BBC否CBD否DBD是注意死状态在DFA中通常省略不画但写转移表时要标注清楚否则容易在最小化时误判等价。2.2 包含01子串的字符串正规表达式与DFA的对应关系第二题要求构造包含01子串的所有二进制串的正规表达式。这个语言的特点是只要串中出现过一次连续的01后面接任意串都满足条件。正规表达式为(0|1)*01(0|1)*。前半部分(0|1)*允许01之前出现任意字符中间01是必须出现的子串后半部分(0|1)*允许01之后出现任意字符。构造DFA时状态设计要跟踪“是否已经看到01”。初始状态q0表示还没看到01读入0到q1看到了0可能是01的前半读入1到q0还是没看到01。q1读入0到q1连续0仍然只看到0读入1到q2看到了01进入接受状态。q2读入0或1都自环因为一旦包含01后续任意字符都不影响结论。这个DFA只有三个状态最小化时q0和q1不等价q1读入1到接受状态q0读入1到自身q2单独一组。最终状态转移表状态输入0输入1是否接受q0q1q0否q1q1q2否q2q2q2是验证方法是拿几个边界串跑一遍空串不包含01拒绝串01从q0读0到q1读1到q2接受串10从q0读1到q0读0到q1最终在q1不是接受状态拒绝。这种手动验证在考试时能快速检查DFA是否正确。3. 逆波兰表示与四元式表达式翻译的两种中间代码形式3.1 逆波兰表示的手工推导与栈操作验证题目要求写出AB*(C-D)E/(C-D)的逆波兰表示。逆波兰表示也叫后缀表达式运算符写在操作数之后不需要括号。手工转换时按运算符优先级和结合性逐步处理先算括号内的C-D得到CD-然后算B*(C-D)得到BCD-*接着算AB*(C-D)得到ABCD-*再算E/(C-D)得到ECD-/最后把两部分相加得到ABCD-*ECD-/。用栈验证从左到右扫描遇到操作数压栈遇到运算符弹出两个操作数计算后压回。扫描A压栈B压栈C压栈D压栈遇到-弹出D和C计算C-D压栈遇到*弹出(C-D)和B计算B*(C-D)压栈遇到弹出B*(C-D)和A计算AB*(C-D)压栈E压栈C压栈D压栈遇到-弹出D和C计算C-D压栈遇到/弹出(C-D)和E计算E/(C-D)压栈最后遇到弹出两部分相加。栈最终只剩一个值验证通过。3.2 四元式序列的生成规则与临时变量命名四元式用四个字段表示运算符、操作数1、操作数2、结果。生成时按逆波兰表示的顺序每遇到一个运算符就产生一条四元式临时变量用T1、T2依次编号。对于AB*(C-D)E/(C-D)生成过程如下(1) - C D T1 (2) * B T1 T2 (3) A T2 T3 (4) - C D T4 (5) / E T4 T5 (6) T3 T5 T6注意第4条四元式重新计算了C-D因为原始表达式中C-D出现了两次而四元式序列没有做公共子表达式消除。如果题目要求优化可以把第4条改为(4) T1 _ T4直接复用T1的值。考试时如果不确定是否要优化按最直接的方式生成即可除非题目明确要求优化。提示四元式中临时变量的编号顺序会影响后续代码生成的寄存器分配实际编译器里会尽量复用临时变量但试卷题目通常只要求正确生成不要求优化。4. 文法二义性与LL(1)分析从证明到预测分析表构造4.1 文法二义性证明的两种路径与语言描述题目给出的文法G开始符号N产生式如下N → SE | E S → SD | D E → 0 | 2 | 4 | 6 | 8 | 10 D → 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9证明二义性需要找到至少两个不同的推导树或最左推导产生同一个句子。观察文法N可以推导出SE或ES可以推导出SD或DE产生偶数包括10D产生单个数字。一个句子比如10可以通过N→E→10得到也可以通过N→SE→DE→1 0得到这里D→1E→0但注意S→D后跟E实际上S→D产生单个数字然后E产生另一个数字拼接成10。两条路径产生同一个串10但语法树不同因此文法有二义性。文法描述的语言是所有由偶数结尾的数字串或者更准确地说所有十进制数字串中最后一个数字是偶数的串。因为E产生偶数0,2,4,6,8,10而S产生任意数字序列N→SE表示任意数字序列后跟一个偶数N→E表示单个偶数。所以语言L(G) { w | w是十进制数字串且w的最后一个字符是偶数 }。注意E中的10是两个字符但作为整体产生10这会导致一些边界情况比如10本身以0结尾符合条件。4.2 First集与Follow集的计算过程题目第四题给出的文法GE → TE E → E | ε T → FT T → T | ε F → PF F → *F | ε P → (E) | ^ | a | b计算First集First(E) First(T) First(F) First(P) { (, ^, a, b }。First(E) { , ε }。First(T) First(T) ∪ { ε } { (, ^, a, b, ε }。First(F) { *, ε }。计算Follow集Follow(E) { ), $ }E是开始符号$加入P→(E)中E后面是)。Follow(E) Follow(E) { ), $ }。Follow(T) First(E) \ {ε} ∪ Follow(E) { , ), $ }。Follow(T) Follow(T) { , ), $ }。Follow(F) First(T) \ {ε} ∪ Follow(T) { (, ^, a, b, , ), $ }。Follow(F) Follow(F) { (, ^, a, b, , ), $ }。Follow(P) First(F) \ {ε} ∪ Follow(F) { *, (, ^, a, b, , ), $ }。4.3 LL(1)文法证明与预测分析表构造证明LL(1)文法需要检查每个非终结符的产生式是否有冲突。对于E→TE只有一条产生式无冲突。E→E | εFirst(E){}First(ε){ε}Follow(E){),$}{}与{),$}不相交无冲突。T→FT单条产生式。T→T | εFirst(T){ (,^,a,b }Follow(T){,),$}不相交。F→PF单条。F→*F | εFirst(F){}Follow(F){ (,^,a,b,,),$ }不相交。P→(E) | ^ | a | b各产生式First集分别为{(}、{^}、{a}、{b}互不相交。因此文法是LL(1)的。构造预测分析表行是非终结符列是终结符。对于每个产生式A→α对每个a∈First(α)在M[A,a]填入A→α如果ε∈First(α)对每个b∈Follow(A)在M[A,b]填入A→α。最终表如下非终结符()*^ab$EE→TEE→TEE→TEE→TEEE→εE→EE→εTT→FTT→FTT→FTT→FTTT→TT→εT→εT→TT→TT→TT→εFF→PFF→PFF→PFF→PFFF→εF→εF→εF→*FF→εF→εF→εF→εPP→(E)P→^P→aP→b注意T→T和T→ε在Follow(T){,),$}上T→T的First集是{(,^,a,b}与Follow集不相交所以表中T行在(、^、a、b列填T→T在、)、$列填T→ε。F行类似列填F→F其他列填F→ε。5. if-then-else的四元式翻译控制流与回填技术5.1 条件语句的四元式生成步骤题目要求把if x0 y0 then z:xy else begin x:x2; y:y3 end;翻译成四元式序列。注意条件部分x0 y0在试卷中可能表示x0 and y0这里按逻辑与处理。四元式生成需要用到回填技术先产生条件跳转指令但跳转目标暂时留空等确定目标地址后再回填。生成过程如下(1) x 0 T1 (2) j T1 _ 3 // 如果T1为假跳到第3条之后 (3) y 0 T2 (4) j T2 _ 7 // 如果T2为假跳到else分支 (5) x y T3 (6) : T3 _ z (7) j _ _ 10 // then分支结束跳过else (8) x 2 T4 (9) : T4 _ x (10) y 3 T5 (11) : T5 _ y (12) ... // 后续语句这里第2条j T1 _ 3表示如果T1为假即x0不成立跳转到第3条之后的位置也就是跳过then分支直接去else。第4条类似如果y0不成立跳到第7条之后。第7条是无条件跳转跳过else分支。注意第8条和第10条的顺序else分支中先执行x:x2再执行y:y3所以四元式按顺序生成。5.2 回填技术的实现逻辑与常见错误回填技术的核心是维护两个列表真出口链和假出口链。当产生条件跳转时把跳转指令的编号加入对应的链中当确定目标位置时遍历链把目标地址填入。手工做题时可以用编号代替地址先写跳转指令最后统一回填。常见错误有三个一是跳转目标算错比如第2条应该跳到else分支的第一条第8条但写成了第3条二是忘记then分支结束后的无条件跳转导致执行完then后继续执行else三是临时变量编号重复比如T1用了两次。避免方法是每生成一条四元式就检查跳转逻辑画一个简单的控制流图辅助验证。提示四元式中的j表示无条件跳转j表示条件跳转条件为假时跳转:表示赋值。不同教材的符号可能略有差异但结构一致。6. 用这套试卷做自测三个验证技巧与一个血泪教训6.1 用边界串验证DFA与正规表达式做完正规表达式和DFA题目后不要只检查最终答案拿几个边界串手动跑一遍DFA。比如第一题的语言是“不以0开头但以11结尾”边界串包括空串拒绝、单字符1拒绝不以11结尾、11接受、011拒绝以0开头、1011接受、11011接受。如果DFA对011返回接受说明首字符约束没处理好。这种验证方法比重新推导一遍快得多而且能发现状态转移表中的笔误。6.2 用栈模拟验证逆波兰表示逆波兰表示做完后用栈模拟一遍计算过程。拿ABCD-*ECD-/为例从左到右扫描遇到操作数压栈遇到运算符弹出两个操作数。如果栈在某个时刻操作数不够弹出说明表达式写错了。这个方法在考试时能快速检查比重新推导优先级快。另外注意逆波兰表示中操作数的顺序和原表达式一致但运算符的顺序由优先级决定不要凭感觉调整。6.3 用预测分析表跑一个输入串构造完LL(1)预测分析表后拿一个输入串比如aa*b跑一遍分析过程。初始栈$E输入aa*b$。查表M[E,a]E→TE弹出E压入ET。继续查表每一步都对照预测分析表。如果某一步查表为空说明输入串不符合文法或者表构造有误。这个方法能同时验证First集、Follow集和预测分析表的正确性。6.4 一个血泪教训不要跳过二义性证明的语法树我刚开始做这类试卷时觉得二义性证明就是找两个推导随便写写就行。结果有一次考试题目要求“证明文法G有二义性”我只写了两个最左推导没有画语法树扣了一半分。后来才明白二义性的本质是同一个句子对应两棵不同的语法树只写推导过程不够直观阅卷老师要看的是语法树的结构差异。从那以后我每次做二义性证明都强制画两棵语法树哪怕题目只要求写推导我也会在旁边附上树形结构。这个习惯让我在后续的编译原理考试里再也没丢过二义性证明的分。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑