编译原理教程课后习题答案——第六章
(完整版)编译原理[张素琴]第2版-答案-清华大学出版社
![(完整版)编译原理[张素琴]第2版-答案-清华大学出版社](https://img.taocdn.com/s3/m/2b7b9004dd88d0d232d46a24.png)
《编译原理》课后习题第 1 章引论第1 题解释下列术语:(1)编译程序:如果源语言为高级语言,目标语言为某台计算机上的汇编语言或机器语言,则此翻译程序称为编译程序。
(2)源程序:源语言编写的程序称为源程序。
(3)目标程序:目标语言书写的程序称为目标程序。
(4)编译程序的前端:它由这样一些阶段组成:这些阶段的工作主要依赖于源语言而与目标机无关。
通常前端包括词法分析、语法分析、语义分析和中间代码生成这些阶段,某些优化工作也可在前端做,也包括与前端每个阶段相关的出错处理工作和符号表管理等工作。
(5)后端:指那些依赖于目标机而一般不依赖源语言,只与中间代码有关的那些阶段,即目标代码生成,以及相关出错处理和符号表操作。
(6)遍:是对源程序或其等价的中间语言程序从头到尾扫视并完成规定任务的过程。
第2 题一个典型的编译程序通常由哪些部分组成?各部分的主要功能是什么?并画出编译程序的总体结构图。
答案:一个典型的编译程序通常包含 8 个组成部分,它们是词法分析程序、语法分析程序、语义分析程序、中间代码生成程序、中间代码优化程序、目标代码生成程序、表格管理程序和错误处理程序。
其各部分的主要功能简述如下。
词法分析程序:输人源程序,拼单词、检查单词和分析单词,输出单词的机内表达形式。
语法分析程序:检查源程序中存在的形式语法错误,输出错误处理信息。
语义分析程序:进行语义检查和分析语义信息,并把分析的结果保存到各类语义信息表中。
中间代码生成程序:按照语义规则,将语法分析程序分析出的语法单位转换成一定形式的中间语言代码,如三元式或四元式。
中间代码优化程序:为了产生高质量的目标代码,对中间代码进行等价变换处理。
目标代码生成程序:将优化后的中间代码程序转换成目标代码程序。
表格管理程序:负责建立、填写和查找等一系列表格工作。
表格的作用是记录源程序的各类信息和编译各阶段的进展情况,编译的每个阶段所需信息多数都从表格中读取,产生的中间结果都记录在相应的表格中。
编译原理第六章到第十一章课后习题答案

编译原理第六章到第十一章课后习题答案p116/1.已知文法G[S]为:S→a|∧|(T)T→T,S|S(1) 计算FIRSTVT -- LASTVT表(2) 构造算符优先关系表(OPERATER PRIORITY RELATION TABLE),说明是否为算符优先文法。
=: #=#, (=)<: (< FIRSTVT(T) , ,<firstvt(s)<="" ,="" p="">>:LASTVT(S)># , LASTVT(T)>), LASTVT(T)> ,表中无多重人口所以是算符优先(OPG)文法。
(3)计算G[S]的优先函数。
收敛(4)对输入串(a,a)#的算符优先分析过程为Success!3.有文法G(S):s->Vv->T/ViTT->F/T+FF->)V*|((1)(+(i(的规范推导S=>V=>ViT=>ViF=>Vi(=>Ti(=>T+Fi(=>T+(i(=>F+(i(=>(+(i((2)F+Fi(的短语、句柄、素短语。
短语S: F+Fi(T1:F+F (素短语)T2:F (句柄)F:( (素短语)(3) G(S)是否为OPG?若是,给出(1)中句子的分析过程!S’->#S# S->V V->T/ViT T->F/T+F F->)V*|(算符优先关系表(OPERATER PRIORITY RELATION TABLE)对输入串(+(I(的算符优先分析过程为:p152/2文法:S→L.L|LL→LB|BB→0|1拓广文法为G′,增加产生式S′→SI3若产生式排序为:0 S' →S1 S →L.L2 S →L3 L →LB4 L →B5 B →06 B →1由产生式知:First (S' ) = {0,1}First (S ) = {0,1}First (L ) = {0,1}First (B ) = {0,1}Follow(S' ) = {#}Follow(S ) = {#}Follow(L ) = {.,0,1,#}Follow(B ) = {.,0,1,#}G′的LR(0)项目集族及识别活前缀的DFA如下图所示:I5B →.0和B →.1为移进项目,S →L.为归约项目,存在移进-归约冲突,因此所给文法不是LR(0)文法。
《编译原理》(陈火旺版)课后作业参考答案ch6-10

第6章属性文法和语法制导翻译7. 下列文法由开始符号S产生一个二进制数,令综合属性val给出该数的值:试设计求的属性文法,其中,已知B的综合属性c, 给出由B产生的二进位的结果值。
例如,输入时,=,其中第一个二进位的值是4,最后一个二进位的值是。
【答案】11. 设下列文法生成变量的类型说明:(1)构造一下翻译模式,把每个标识符的类型存入符号表;参考例。
【答案】第7章语义分析和中间代码产生1. 给出下面表达式的逆波兰表示(后缀式):【答案】3. 请将表达式-(a+b)*(c+d)-(a+b+c)分别表示成三元式、间接三元式和四元式序列。
【答案】间接码表:(1)→(2)→(3)→(4)→(1)→(5)→(6)4. 按节所说的办法,写出下面赋值句A:=B*(-C+D) 的自下而上语法制导翻译过程。
给出所产生的三地址代码。
【答案】5. 按照7.3.2节所给的翻译模式,把下列赋值句翻译为三地址代码:A[i, j]:=B[i, j] + C[A [k, l]] + d[ i+j]【答案】6. 按7.4.1和节的翻译办法,分别写出布尔式A or ( B and not (C or D) )的四元式序列。
【答案】用作数值计算时产生的四元式:用作条件控制时产生的四元式:其中:右图中(1)和(8)为真出口,(4)(5)(7)为假出口。
7. 用7.5.1节的办法,把下面的语句翻译成四元式序列: While A<C and B<D do if A=1 then C:=C+1 else while A ≦D do A:=A+2; 【答案】第9章 运行时存储空间组织4. 下面是一个Pascal 程序:当第二次( 递归地) 进入F 后,DISPLAY 的内容是什么当时整个运行栈的内容是什么 【答案】第1次进入F 后,运行栈的内容: 第2次进入F 后,运行栈的内容: 109 87 6 5 4 3 2 1 017 1615 14 13 12 11 10 9 8 7 6 5第2次进入F 后,Display 内容为:5. 对如下的Pascal 程序,画出程序执行到(1)和(2)点时的运行栈。
编译原理及其习题解答(武汉大学出版社)课件chap6

练习1题目 练习 题目
文法G[T]: T→ F | T*F F →F ↑ P | P P→ (T) | i 证明T*P ↑(T*F)是文法G的一个句型,并 指出这个句型的所有短语、直接短语、 句柄。
编译原理 Compiler Principles
练习1 练习1解答
证明:T T*F T*F↑P T*F↑(T) 语法树: T T * F P F ↑ ( P T )
编译原理 Compiler Principles
练习2题目
设有文法G[S]: 设有文法 S →V1 V1→ V2 | V1iV2 V2→ V3 | V2+V3 V3→ )V1* | ( (1)给出 (+(i( 的最右推导,并画出相应的语法树; 给出 的最右推导,并画出相应的语法树; (2)证明 2+V3i( 是文法的一个句型,并指出这个句型的短语、直接 证明V 是文法的一个句型,并指出这个句型的短语、 证明 短语、句柄。 短语、句柄。
编译原理 Compiler Principles
非确定的自下而上的分析器
非确定的自下而上的分析器,是一般移进 归约方法 非确定的自下而上的分析器,是一般移进-归约方法 的抽象模型,可识别任何上下文无关语言。 的抽象模型,可识别任何上下文无关语言。给定一个上下 文无关文法,可构造一个自下而上的分析器。 文无关文法,可构造一个自下而上的分析器。 非确定的自下而上的分析器与非确定的自上而下的分 析器的不同之处: 析器的不同之处: 课本P147 课本
编译原理 Compiler Principles
自下而上分析法存在的问题
可归约串的问题;(∵ 该分析的每一步就是从当前串中找一 ;(∵
个子串( 个子串(称“可归约串”),将它归约到某个非终结符号) 可归约串”),将它归约到某个非终结符号) 将它归约到某个非终结符号 自下而上分析法的关键就是找哪个子串是“可归约串” 自下而上分析法的关键就是找哪个子串是“可归约串”, 关键就是找哪个子串是 哪个不是“可归约串” 例如上例中的(3) 哪个不是“可归约串”。例如上例中的
编译原理第6章习题与答案

第6章习题6-1 将下列中缀式改写为逆波兰式。
(1) -A*(B+C)/(D-E)(2) ((a*d+c)/d+e)*f+g(3) a+x≤4∨(c>d*3)(4) a∨b∧c<d*e/f6-2 将下列逆波兰式改写为中缀式。
(1) abc*+(2) abc-*cd+e/-(3) abc+≤a0>∧ab+0≠a0<∧∨6-3 将下列语句翻译成四元式序列。
(1) X:=A*(B+C)+D(2) if A∧(B∨(C∨D)) then S1 else S2(3) while A<C∧B>0 doif A=1 then C:=C+1 else A:=A+26-4 设有二维PASCAL数组A[1··10,1··20]和三维PASCAL数组B[1··10, 1··20,1··30],给出赋值语句A[I,J]:=B[J,I+J,I+1]+X的四元式序列。
第5章习题答案6-1 解:(1) A-BC+*DE-/(2) ad*c+d/e+f*g+(3) ax+4≤cd3*>∨(4) abcde*f/<∧∨6-2 解:(1) a+b*c(2) a*(b-c)-(c+d)/e(3) a≤b+c∧a>0∨a+b≠0∧a<06-3 解:(1) (1) (+,B,C,T1)(2) (*,A,T1 ,T2)(3) (+,T2 ,D,T3)(4) (=,T3 ,0,X)(2) 如下所示:(1) (jnz,A,0,3);(2) (j,0,0,p+1);(3) (jnz,B,0,9);(4) (j,0,0,5);(5) (jnz,C,0,9);(6) (j,0,0,7);(7) (jnz,D,0,9);(8) (j,0,0,p+1);(9) 与S1相应的四元式序列(p) (j,0,0,q)(p+1) 与S2相应的四元式序列(q) …(3) 假设所产生的四元式序列编号从1开始(1) (j<A,C,3)(2) (j,0,0,13)(3) (j>,B,0,5)(4) (j,0,0,13)(5) (j=,A,1,7)(6) (j,0,0,10)(7) (+,C,1,T1)(8) (=,T1 , ,C)(9) (j,0,0,1)(10) (+,A,2,T2)(11) (=,T2 , ,A)(12) (j,0,0,1)(13) …6-4 解:(1) (*,I,20,T1)(2) (+,J,T1,T1)(3) (-,a A,C A ,T2)(4) (+,I,J,T3)(5) (*,J,20,T4)(6) (+,T3 ,T4 ,T4)(7) (+,I,1,T5)(8) (*,T4,30,T6)(9) (+,T5 ,T6 ,T6)(10) (-,a B,C B ,T7)(11) (=[],T7[T6],0,T8)(12) (+,T8 ,X,T9)(13) ([]=,T9 ,0,T2[T1])(注:(1)~(3)是计算下标变量A[I,J]XXX的四元式,T2中存放的是CONSTPART部分,而T1中存放的是VARPART部分,a A表示数组A的首XXX;(4)~(10) 是计算下标变量B[J,I+J,I+1]XXX的四元式,T7中存放的是CONSTPART 部分,而T6中存放的是VARPART部分,a B表示数组B的首XXX。
《编译原理》(清华大学出版社第二版)课后习题答案

PL/0编译程序所产生的目标代码是一种假想栈式计算机的汇编语言,请说明该汇编语言中下列指令各自的功能和所完成的操作。
(1)INT 0 A
(2)OPR 0 0
(3)CAL L A
答案:
PL/0编译程序所产生的目标代码中有3条非常重要的特殊指令,这3条指令在code中的位置和功能以及所完成的操作说明如下:
或者:允许0开头的非负整数?
第3题
为只包含数字、加号和减号的表达式,例如9-2+5,3-1,7等构造一个文法。
答案:
G[S]:
S->S+D|S-D|D
D->0|1|2|3|4|5|6|7|8|9
第4题
已知文法G[Z]:
Z→aZb|ab
写出L(G[Z])的全部元素。
答案:
Z=>aZb=>aaZbb=>aaa..Z...bbb=> aaa..ab...bbb
N→D|1|3|5|7|9
D→2|4|6|8
F→N|0
G→D|0
第6题
已知文法G:
<表达式>::=<项>|<表达式>+<项>
<项>::=<因子>|<项>*<因子>
<因子>::=(<表达式>)|i
试给出下述表达式的推导及语法树。
(5)i+(i+i)
(6)i+i*i
答案:
(5) <表达式>
=><表达式>+<项>
n n
L(G[Z])={a b |n>=1}
第5题写一文法,使其语言是偶正整数的集合。要求:
编译原理部分课后答案,仅供参考

第一章编译程序概述1.1什么是编译程序编译程序是现代计算机系统的基本组成部分之一,而且多 数计算机系统都含有不止一个高级语言的编译程序。
对有些高 级语言甚至配置了几个不同性能的编译程序。
1.2编译过程概述和编译程序的结构编译程序完成从源程序到目标程序的翻译工作,是一个复 杂的整体的过程。
从概念上来讲,一个编译程序的整个工作过 程是划分成阶段进行的,每个阶段将源程序的一种表示形式转 换成另一种表示形式,各个阶段进行的操作在逻辑上是紧密连 接在一起的。
一般一个编译过程划分成词法分析、语法分析、 语义分析、中间代码生成,代码优化和目标代码生成六个阶段,这是一种典型的划分方法。
事实上,某些阶段可能组合在一起, 这些阶段间的源程序的中间表示形式就没必要构造岀来了。
我 们将分别介绍各阶段的任务。
另外两个重要的工作:表格管理 和岀错处理与上述六个阶段都有联系。
编译过程中源程序的各 种信息被保留在种种不同的表格里,编译各阶段的工作都涉及 到构造、查找或更新有关的表格,因此需要有表格管理的工作; 如果编译过程中发现源程序有错误,编译程序应报告错误的性 质和错误发生的地点,并且将错误所造成的影响限制在尽可能 小的范围内,使得源程序的其余部分能继续被编译下去,有些 编译程序还能自动校正错误, 这些工作称之为岀错处理。
图1.3表示了编译的各个阶段。
图1.3编译的各个阶段它不生成目标代码,它每遇到一个语句,就要对这个语句进行 分析以决定语句的含义,执行相应的动作。
右面的图示意了它 的工作机理第二章:PL/O 编译程序问答第1题 PL/0语言允许过程嵌套定义和递归调用,试问 它的编译程序如何解决运行时的存储管理。
答:PL/0语言允许过程嵌套定义和递归调用,它的编译程序在运行时采用了栈式动态存储管理。
(数组CODE 存放的只读目 标程序,它在运行时不改变。
)运行时的数据区S 是由解释程序 定义的一维整型数组,解释执行时对数据空间S 的管理遵循后进先岀规则,当每个过程(包括主程序)被调用时,才分配数据 空间,退出过程时,则所分配的数据空间被释放。
编译原理课后习题答案-清华大学-第二版

《编译原理》课后习题答案第二章
第 2 章 PL/0 编译程序的实现
第1题
PL/0 语言允许过程嵌套定义和递归调用,试问它的编译程序如何解决运行时的存储管 理。
答案: PL/0 语言允许过程嵌套定义和递归调用,它的编译程序在运行时采用了栈式动态存储
管理。(数组 CODE 存放的只读目标程序,它在运行时不改变。)运行时的数据区 S 是由 解释程序定义的一维整型数组,解释执行时对数据空间 S 的管理遵循后进先出规则,当每 个过程(包括主程序)被调用时,才分配数据空间,退出过程时,则所分配的数据空间被释放。 应用动态链和静态链的方式分别解决递归调用和非局部变量的引用问题。
(2) 扩充 repeat 语句的语法图为:
EBNF 的语法描述为: 〈 重复语句〉::= repeat〈语句〉{;〈语句〉}until〈条件〉
《编译原理》课后习题答案第三章
第 3 章 文法和语言
第1题
文法 G=({A,B,S},{a,b,c},P,S)其中 P 为: S→Ac|aB A→ab B→bc 写出 L(G[S])的全部元素。
注意:如果问编译程序有哪些主要构成成分,只要回答六部分就可以。如果搞不清楚, 就回答八部分。 第3题
何谓翻译程序、编译程序和解释程序?它们三者之间有何种关系? 答案:
翻译程序是指将用某种语言编写的程序转换成另一种语言形式的程序的程序,如编译程 序和汇编程序等。
编译程序是把用高级语言编写的源程序转换(加工)成与之等价的另一种用低级语言编 写的目标程序的翻译程序。
地址,用以过程执行结束后返回调用过程时的下一条指令继续执行。 在每个过程被调用时在栈顶分配 3 个联系单元,用以存放 SL,DL, RA。
第5题
PL/0 编译程序所产生的目标代码是一种假想栈式计算机的汇编语言,请说明该汇编语 言中下列指令各自的功能和所完成的操作。 (1) INT 0 A (2) OPR 0 0 (3) CAL L A
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第六章运行时存储空间组织
6.1 完成下列选择题:
(1) 过程的DISPLAY表中记录了。
a. 过程的连接数据
b. 过程的嵌套层次
c. 过程的返回地址
d. 过程的入口地址
(2) 过程P1调用P2时,连接数据不包含。
a. 嵌套层次显示表
b. 老SP
c. 返回地址
d. 全局DISPLAY地址
(3) 堆式动态分配申请和释放存储空间遵守原则。
a. 先请先放
b. 先请后放
c. 后请先放
d. 任意
(4) 栈式动态分配与管理在过程返回时应做的工作有。
a. 保护SP
b. 恢复SP
c. 保护TOP
d. 恢复TOP
(5) 如果活动记录中没有DISPLAY表,则说明。
a. 程序中不允许有递归定义的过程
b. 程序中不允许有嵌套定义的过程
c. 程序中既不允许有嵌套定义的过程,也不允许有递归定义的过程
d. 程序中允许有递归定义的过程,也允许有嵌套定义的过程
【解答】
(1) b (2) a
(3) d (4) b (5) b
6.2 何谓嵌套过程语言运行时的DISPLAY表?它的作用是什么?
【解答】当过程定义允许嵌套时,一个过程在运行中应能够引用在静态定义时包围它的任一外层过程所定义的变量或数组。
也就是说,在栈式动态存储分配方式下的运行中,一个过程Q可能引用它的任一外层过程P的最新活动记录中的某些数据。
因此,过程Q运行时必须知道它的所有(静态)外层过程的最新活动记录的地址。
由于允许递归和可变数组,这些外层过程的活动记录的位置也往往是变迁的。
因此,必须设法跟踪每个(静态)外层的最新活动记录的位置,而完成这一功能的就是DISPLAY嵌套层次显示表。
也即,每当进入一个过程后,在建立它的活动记录区的同时也建立一张DISPLAY表,它自顶而下每个单元依次存放着现行层、直接外层等,直至最外层(主程序层)等每一层过程的最新活动记录的起始地址。
6.3 (1) 写出实现一般递归过程的活动记录结构以及过程调用、过程进入与过程返回的指令;
(2) 对以return(表达式)形式(这个表达式本身是一个递归调用)返回函数值的特殊函数过程,给出不增加时间开销但能节省存储空间的实现方法。
假定语言中过程参数只有传值和传地址两种形式,为便于理解,举下例说明这种特殊的函数调用:
int gcd (int p,int q)
{
if (p % q ==0) return q;
else return gcd (q, p % q)
}
【解答】(1) 一般递归过程的活动记录如图6-1所示。
TOP
图6-1 递归过程的活动记录过程调用指令为
(i+4)[TOP]=Ti 或(i+4)[TOP]=addr [Ti]
1[TOP]=SP
3[TOP]=SP+d
4[TOP]=n
JSR P
过程进入指令为
SP=TOP+1
1[SP]=返回地址
TOP=TOP+L
建立DISPLAY
P;/*执行P过程*/
返回指令为
TOP=SP-1
SP=0[SP]
X=2[TOP]
UJ 0[X]
(2) 对于return后的直接递归情况,可简化为
(i+3)[SP]=Ti 或(i+3)[SP]=addr [Ti]
UJ P
6.4 有一程序如下:
program ex;
a: integer;
procedure PP(x: integer);
begin:
x:=5; x:=a+1
end;
begin a:= 2; PP(a); write(a) end. 试用图表示ex 调用PP(a)前后活动记录的过程。
【解答】 按照嵌套过程语言栈式实现方法,ex 调用PP(a)前后活动记录的过程如图6-2所示。
图6-2 ex 调用PP(a)前后的活动记录
6.5 类PASCAL 结构(嵌套过程)的程序如下,该语言的编译器采用栈式动态存储分配策略管理目标程序数据空间。
program Demo procedure A; procedure B; begin(*B*) … if d then B else A; …
end;(*B*) begin(*A*) B end;(*A*)
begin(*Demo*) A end.
(1) 若过程调用序列为
PP 的活动记录(调用PP(a)之后)
ex 的活动记录(调用PP(a)之前)
① Demo →A ;② Demo →A →B ;③ Demo →A →B →B ;④ Demo →A →B →B →A 请分别给出这四个时刻运行栈的布局和使用的DISPLAY 表; (2) 若该语言允许动态数组,编译程序应如何处置?如过程B 有动态局部数组R[m:n],请给出B 第一次激活时相应的数据空间的情况。
【解答】 (1) 运行栈及使用的DISPLAY 表如图6-3所示。
图6-3 运行栈及DISPLAY 表示意图
(2) 由于一个过程在运行时所需的实际数据空间的大小,除可变数据结构(可变数组)那些部分外,其余部分在编译时是完全可以知道的。
编译程序处理时将过程运行时所需的数据空间分为两部分:一部分在编译时可确定其体积,称为该过程的活动记录;另一部分(动态数组)
A 的活动记录Demo 的活动记录
B 的活动记录
A 的活动记录Demo 的活动记录(1) Demo→A (2) Demo→A→B
Demo_sp
DISPLAY 表A 2(3) Demo→A→B 1→B 2A 2的活动记录A 1的活动记录Demo 的活动记录
(4) Demo→A 1→B 1→B 2→A 2B 2的活动记录B 1的活动记录B 2B 1A 1B 2_sp
DISPLAY B 1_sp
A _sp
B 1的活动记录B 2的活动记录A 的活动记录Demo 的活动记录
的体积需在运行时动态确定,称为该过程的可变辅助空间。
当一个过程开始工作时,首先在运行栈顶部建立它的活动记录,然后再在这个记录之顶确定它所需的辅助空间。
含有动态数组R 的过程B 在第一次激活时,相应的数据空间情况如图6-4所示。
图6-4 带动态数组的运行栈示意
(a) 动态数组R 空间分配之前;(b) 动态数组R 空间分配之后
6.6 下面程序的结果是120。
但是如果把第5行的abs(1)改成1的话,则程序结果为1。
试分析为什么会有这种不同的结果。
int fact( ) { static int i=5; if (i==0){ return(1); } else { i=i-1; return((i+abs(1))*fact( ));} } main( ) { printf ("factor or 5=%d\n",fact( )); } 解答】 i 是静态变量,所有对i 的操作实际上都是对i 所对应的存储单元进行操作,每次递归进入下一层fact 函数后,上一层对i 的赋值仍然有效。
需要注意的是,每次递归调用时,(i + abs(1))*fact( )中的(i + abs(1))的值都先于fact 算出。
因此,第一次递归调用所求得的值为5*fact ,第二次递归调用所求得的值为4*fact ,…,一直到第五次递归调用所求得的值为1*fact ,而此时fact 为1。
也即实际上是求一个5*4*3*2*1的阶乘,由此得到结果
B 的活动记录
A_sp
A 的活动记录Demo 的活动记录
B 的活动记录A 的活动记录
的活动记录B 的可变辅助空间(a)(b)
为120。
将abs(1)改为1后,输出结果为1而不是120,这主要是与编译的代码生成策略有关。
对表达式(i + abs(1))* fact( ),因为两个子表达式(i+abs(1))和fact( )都有函数调用,而编译器的编译则是先产生左子表达式的代码,后产生右子表达式的代码。
也即,每次递归调用时,(i + abs(1))* fact( )中的(i+abs(1))的值都先于fact算出。
但是,当abs(1)改为1后,左子表达式就没有函数调用了,于是编译器就先产生右子表达式的代码。
每次递归调用时,(i+1)* fact( )中的(i+1)值都后于fact计算。
也即,第一次递归调用得到(i+1)* fact,第二次递归调用得到(i+1)*fact,第三次递归调用仍得到(i+1)*fact,…,直到第五次递归调用还是得到(i+1)* fact,而此时fact为1,i为0。
因此,每次递归所求实际上都是1 * fact,最终得到输出结果为1。