Presentation is loading. Please wait.

Presentation is loading. Please wait.

习题课 编译原理与技术 计算机科学与技术学院 李诚.

Similar presentations


Presentation on theme: "习题课 编译原理与技术 计算机科学与技术学院 李诚."— Presentation transcript:

1 习题课 编译原理与技术 计算机科学与技术学院 李诚

2 Assignment 11 4.15

3 Assignment 11 – 4.15 题目:下面是构造语法树的一个S属性定义。将这里 的语义规则翻译成LR翻译器的栈操作代码段。
𝐸→ 𝐸 1 +𝑇 𝐸.𝑛𝑝𝑡𝑟=𝑚𝑘𝑁𝑜𝑑𝑒(′ + ′ ,𝐸1.𝑛𝑝𝑡𝑟,𝑇.𝑛𝑝𝑡𝑟) 𝐸→ 𝐸 1 −𝑇 𝐸.𝑛𝑝𝑡𝑟=𝑚𝑘𝑁𝑜𝑑𝑒(′ − ′ ,𝐸1.𝑛𝑝𝑡𝑟,𝑇.𝑛𝑝𝑡𝑟) 𝐸→𝑇 𝐸.𝑛𝑝𝑡𝑟=𝑇.𝑛𝑝𝑡𝑟 𝑇→ 𝐸 𝑇.𝑛𝑝𝑡𝑟=𝐸.𝑛𝑝𝑡𝑟 𝑇→𝐢𝐝 𝑇.𝑛𝑝𝑡𝑟=𝑚𝑘𝐿𝑒𝑎𝑓(𝐢𝐝,𝐢𝐝.𝑒𝑛𝑡𝑟𝑦) 𝑇→𝐧𝐮𝐦 𝑇.𝑛𝑝𝑡𝑟=𝑚𝑘𝐿𝑒𝑎𝑓(𝐧𝐮𝐦,𝐧𝐮𝐦.𝑒𝑛𝑡𝑟𝑦)

4 注意:栈操作代码段不是SDT(语法制导翻译方案)!
Assignment 11 – 4.15 产生式 代码段 𝐸→ 𝐸 1 +𝑇 s𝑡𝑎𝑐𝑘 𝑡𝑜𝑝−2 .𝑣𝑎𝑙=𝑠𝑡𝑎𝑐𝑘 𝑡𝑜𝑝−2 .𝑣𝑎𝑙+𝑠𝑡𝑎𝑐𝑘 𝑡𝑜𝑝 .𝑣𝑎𝑙 𝐸→ 𝐸 1 −𝑇 s𝑡𝑎𝑐𝑘 𝑡𝑜𝑝−2 .𝑣𝑎𝑙=𝑠𝑡𝑎𝑐𝑘 𝑡𝑜𝑝−2 .𝑣𝑎𝑙−𝑠𝑡𝑎𝑐𝑘 𝑡𝑜𝑝 .𝑣𝑎𝑙 𝐸→𝑇 𝑇→ 𝐸 𝑠𝑡𝑎𝑐𝑘 𝑡𝑜𝑝−2 .𝑣𝑎𝑙=𝑠𝑡𝑎𝑐𝑘 𝑡𝑜𝑝−1 .𝑣𝑎𝑙 𝑇→𝐢𝐝 𝑇→𝐧𝐮𝐦 注意:栈操作代码段不是SDT(语法制导翻译方案)!

5 Assignment 12 附加题

6 Assignment 12 – 附加题 题目:考虑以下消除左递归后的算数表达式文法 𝐸→𝑇𝑅 𝑅→+𝑇𝑅1 𝑅→𝜀
𝐸→𝑇𝑅 𝑅→+𝑇𝑅 𝑅→𝜀 𝑇→𝐹𝑊 𝑊→∗𝐹𝑊1 𝑊→𝜀 𝐹→ 𝐸 𝐹→𝐢𝐝 𝐹→𝐧𝐮𝐦 写一个语法制导定义,计算表达式的值(提示: 需要使用继承 属性) 为上述语法制导定义写一个语法制导翻译方案 将语法制导定义中的语义规则翻译成LR分析器 的栈操作代码

7 Assignment 12 – 附加题 产生式 语义规则 𝐸→𝑇𝑅 𝑅.𝑖=𝑇.𝑛𝑝𝑡𝑟 𝐸.𝑛𝑝𝑡𝑟=𝑅.𝑠 𝑅→+𝑇𝑅1
𝑅.𝑖=𝑇.𝑛𝑝𝑡𝑟 𝐸.𝑛𝑝𝑡𝑟=𝑅.𝑠 𝑅→+𝑇𝑅1 𝑅1 .𝑖=𝑚𝑘𝑁𝑜𝑑𝑒( ‘+’,𝑅.𝑖, 𝑇.𝑛𝑝𝑡𝑟) 𝑅.𝑠 = 𝑅1 .𝑠 𝑅→𝜀 𝑅.𝑠=𝑅.𝑖 𝑇→𝐹𝑊 (同上) 𝑊→∗𝐹𝑊1 𝑊→𝜀 𝐹→ 𝐸 𝐹.𝑛𝑝𝑡𝑟=𝐸.𝑛𝑝𝑡𝑟 𝐹→𝐢𝐝 𝐹.𝑛𝑝𝑡𝑟=𝑚𝑘𝐿𝑒𝑎𝑓(𝐢𝐝, 𝐢𝐝.𝑒𝑛𝑡𝑟𝑦) 𝐹→𝐧𝐮𝐦 𝐹.𝑛𝑝𝑡𝑟=𝑚𝑘𝐿𝑒𝑎𝑓(𝐧𝐮𝐦,𝐧𝐮𝐦.𝑣𝑎𝑙)

8 Assignment 13

9 Assignment 13 – 5.4 为下列类型写类型表达式: 指向实数的指针数组,数组的下标从0到99
二维数组(即元素类型为一维数组的数组),它的行下标从0到 9,列下标从0到19 函数,它的定义是从整数到整数指针的函数,它的值域是由一 个整数和一个字符组成的记录。

10 Assignment 13 – 5.4 Array(0..99, real)
Array(0..9, pointer(array(0..19, elemtype))) Function(Function(integer→pointer(integer)) →record(integer*character))

11 Assignment 13 – 5.5 5.5 假如有下列C的声明: typedef struct{ int a, b;
} CELL, *PCELL; CELL foo[100]; PCELL bar(x, y) int x; CELL y; {} 为变量foo和函数bar的类型写出类型表达式。

12 Assignment 13 – 5.5 array(0..99, record((a × integer) ×(b × integer)))
(integer ×record((a × integer) ×(b × integer))) → pointer(record((a × integer) ×(b × integer)))

13 Assignment 13 – 5.6 下列方法定义字面常量表的表。 𝑃→𝐷;𝐸 𝐷→𝐷;𝐷|𝐢𝐝:𝑇
𝑇→𝐥𝐢𝐬𝐭 𝐨𝐟 𝑇 𝐜𝐡𝐚𝐫 𝐢𝐧𝐭𝐞𝐠𝐞𝐫 𝐸→ 𝐿 𝐥𝐢𝐭𝐞𝐫𝐚𝐥 𝐧𝐮𝐦|𝐢𝐝 𝐿→𝐸,𝐿|𝐸 写一个类似5.3节中的翻译方案,以确定表达式(𝐸)和表(𝐿)的类型。

14 Assignment 13 – 5.6 (仿照5.3.3节) 𝑇→𝑙𝑖𝑠𝑡 𝑜𝑓 𝑇1 {𝑇.𝑡𝑦𝑝𝑒=𝑙𝑖𝑠𝑡(𝑇1.𝑡𝑦𝑝𝑒)}

15 Assignment 14 7.1 把算术表达式− 𝑎+𝑏 ∗ 𝑐+𝑑 +(𝑎+𝑏+𝑐)翻译成: 语法树 有向无环图 后缀表示 三地址代码

16 Assignment 14 – 7.1 + * + - + + c + c d a b a b

17 Assignment 14 – 7.1 + * + - + c + c d a b

18 Assignment 14 – 7.1 后缀表示:ab+-cd+*ab+c++ 三地址代码: t1=a+b t2=-t1 t3=c+d

19 Assignment 15 b

20 Assignment 15 – 5.12 5.12 拓展5.3.3节的类型检查,使之能包含记录。有关记录部分的 类型和记录域引用表达式的语法如下: T→ 𝒓𝒆𝒄𝒐𝒓𝒅 𝑓𝑖𝑒𝑙𝑑𝑠 𝐞𝐧𝐝 𝑓𝑖𝑒𝑙𝑑𝑠→ 𝑓𝑖𝑒𝑙𝑑𝑠;𝑓𝑖𝑒𝑙𝑑 | 𝑓𝑖𝑒𝑙𝑑 𝑓𝑖𝑒𝑙𝑑→𝐢𝐝 :𝑇 𝐸→𝐸.𝐢𝐝

21 Assignment 15 – 5.12 5.12 拓展5.3.3节的类型检查,使之能包含记录。有关记录部分的类 型和记录域引用表达式的语法如下: T→ 𝒓𝒆𝒄𝒐𝒓𝒅 𝑓𝑖𝑒𝑙𝑑𝑠 𝐞𝐧𝐝 {T.type = record(fields.type)} 𝑓𝑖𝑒𝑙𝑑𝑠→ 𝑓𝑖𝑒𝑙𝑑𝑠;𝑓𝑖𝑒𝑙𝑑 {fields.type = fields.type × field.type} 𝑓𝑖𝑒𝑙𝑑𝑠→ 𝑓𝑖𝑒𝑙𝑑 {fields.type = field.type} 𝑓𝑖𝑒𝑙𝑑→𝐢𝐝 :𝑇 {field.type = id.name × T.type} 𝐸→ 𝐸 1 .𝐢𝐝 {E.type = if(E1.type == record(t)) lookup(E1.type, id.name) else type_error;}

22 Assignment 15 – 5.13 5.13在文件stdlib.h中,关 于qsort的外部声明如下:
#include <stdlib.h> typedef struct{ int Ave; double Prob; }HYPO; HYPO astHypo; int n; int HypoCompare(HYPO stHypo1, HYPO stHypo2) { if (stHypo1->Prob>stHypo2->Prob){ return(-1); }else if (stHypo1->Prob<stHypo2->Prob) { return(1); }else{ return(0); } }/ end of function HypoCompare / main() qsort ( astHypo,n,sizeof(HYPO),HypoCompare); 5.13在文件stdlib.h中,关 于qsort的外部声明如下: extern void qsort(void *, size_t, size_t, int (*)(const void *, const void *)); 用SPARC/Solaris C编译器编 译下面的C程序时,错误信 息如下: type.c:24: warning: passing argument 4 of `qsort’ from incompatible pointer type 请你对该程序略作修改,使 得该警告错误能消失,并且 不改变程序的结果。

23 Assignment 15 – 5.13 5.13在文件stdlib.h中, 关于qsort的外部声明 如下:
#include <stdlib.h> typedef struct{ int Ave; double Prob; }HYPO; HYPO astHypo; int n; int HypoCompare(HYPO stHypo1, HYPO stHypo2) { if (stHypo1->Prob>stHypo2->Prob){ return(-1); }else if (stHypo1->Prob<stHypo2->Prob) { return(1); }else{ return(0); } }/ end of function HypoCompare / main() qsort ( astHypo, n, sizeof(HYPO), HypoCompare); 5.13在文件stdlib.h中, 关于qsort的外部声明 如下: extern void qsort(void *, size_t, size_t, int (*)(const void *, const void *)); 问题:qsort的第四个形 式参数类型与函数调用 的传参类型不一致

24 Assignment 15 – 5.13 5.13在文件stdlib.h中, 关于qsort的外部声明 如下:
extern void qsort(void *, size_t, size_t, int (*)(const void *, const void *)); 问题:qsort的第四个形 式参数类型与函数调用 的传参类型不一致 方法一:修改 HypoCompare函数形式 参数的类型 #include <stdlib.h> typedef struct{ int Ave; double Prob; }HYPO; HYPO astHypo; int n; int HypoCompare(const void stHypo1, const voidstHypo2) { if ((HYPO *)stHypo1->Prob>(HYPO *)stHypo2->Prob){ return(-1); }else if ((HYPO *)stHypo1->Prob<(HYPO *)stHypo2->Prob) { return(1); }else{ return(0); } }/ end of function HypoCompare / main() qsort ( astHypo, n, sizeof(HYPO), HypoCompare);

25 Assignment 15 – 5.13 5.13在文件stdlib.h中, 关于qsort的外部声明 如下:
#include <stdlib.h> typedef struct{ int Ave; double Prob; }HYPO; HYPO astHypo; int n; int HypoCompare(HYPO stHypo1, HYPO stHypo2) { if (stHypo1->Prob>stHypo2->Prob){ return(-1); }else if (stHypo1->Prob<stHypo2->Prob) { return(1); }else{ return(0); } }/ end of function HypoCompare / main() qsort ( astHypo, n, sizeof(HYPO), int (*)(const void *, const void *) HypoCompare); 5.13在文件stdlib.h中, 关于qsort的外部声明 如下: extern void qsort(void *, size_t, size_t, int (*)(const void *, const void *)); 问题:qsort的第四个形 式参数类型与函数调用 的传参类型不一致 方法二:强制修改qsort 函数调用中第四个参数 的类型

26 Assignment 15 – 5.9b 5.9 修改5.3.3节的翻译方案,使之能处理布尔表达式。加上逻辑 算符and, or及not和关系算符的产生式。然后给出适当的翻译规 则,它们检查这些表达式的类型。

27 Assignment 15 – 5.9b 产生式略。(仿照书5.3.3小节来写) 检查类型的翻译规则核心:
都是布尔值时才能做and,or,not操作,否则类型错误。

28 Assignment 15 – 5.21 5.21 使用例5.9的规则,确定下列哪些表达式有唯一类型(假定z 是复数): (a) 123
(b) 1  (z 2) (c) (1  z )  z

29 Assignment 15 – 5.21 5.21 使用例5.9的规则,确定下列哪些表达式有唯一类型(假定z 是复数): (a) 123
(b) 1  (z 2) (c) (1  z )  z 运算规则: int  int -> int int  int -> complex complex  complex -> complex

30 Assignment 15 – 5.21 5.21 使用例5.9的规则,确定下列哪些 表达式有唯一类型(假定z是复数): (a) 123
(b) 1  (z 2) (c) (1  z )  z 运算规则: int  int -> int int  int -> complex complex  complex -> complex 1*2*3:{int, complex} 1*2:{int, complex} *: {i  i -> i, i  i -> c, c  c -> c} 3:{int, complex} 1:{int, complex} 2:{int, complex} 类型不唯一 *: {i  i -> i, i  i -> c, c  c -> c}

31 Assignment 15 – 5.21 5.21 使用例5.9的规则,确定下列哪些 表达式有唯一类型(假定z是复数): (a) 123
(b) 1  (z 2) (c) (1  z )  z 运算规则: int  int -> int int  int -> complex complex  complex -> complex 1  (z 2) :{complex} 1:{int} *: {i  i -> i, i  i -> c, c  c -> c} z*2:{complex} z:{complex} *: {i X i -> i, i X i -> c, c X c -> c} 2:{int, complex} 类型唯一

32 Assignment 15 – 5.21 5.21 使用例5.9的规则,确定下列哪些 表达式有唯一类型(假定z是复数): (a) 123
(b) 1  (z 2) (c) (1  z )  z 运算规则: int  int -> int int  int -> complex complex  complex -> complex (1  z )  z:{complex} 1*z:{complex} *: {i  i -> i, i  i -> c, c  c -> c} z:{complex} 1:{int, complex} *: {i  i -> i, i  i -> c, c  c -> c} z:{complex} 类型唯一

33 Assignment 15 – 7.2 7.2 把C程序 main() { int i; int a[10];
while ( i <= 10) a[i] = 0; } 的可执行语句翻译成: 语法树、后缀表示、三地址代码

34 Assignment 15 – 7.2 语法树图略。 后缀表达式:i 10 ≤ a i array 0 = while
if i ≤ 10 goto 3 goto 5 a[i] = 0 goto 1 return 0

35 Assignment 16—7.5 修改7.5中的翻译方案,使得offset不是全局变量而是继承属性
P -> {D.offset1 =0;} D {P.offset = D.offset2 ;} D-> {D1.offset1= D.offset1 ;} D1; {D2.offset1= D1.offset1 ;} D2; {D.offset2= D2.offset2 ;} D-> id : T {enter(id.name, T.type, D.offset1); D.offset2= D.offset1 + T.width ;} T-> intergr {T.type = intergr; T.width = 4} T-> real {T.type = real; T.width = 8} D 的 offset1 为继承属性,表示分析前D所使用变量的offset大小 D 的 offset2 为综合属性,表示分析后D所使用变量的offset大小 P 的 offset 为综合属性,记录该过程所分配的空间

36 Assignment 17—7.16 补全图7.3中赋值语句A[x,y]:=z注释分析书的属性值与每步规约的中间代码

37 Assignment 17—7.16 补全图7.3中赋值语句A[x,y]:=z注释分析书的属性值与每步规约的中间代码 t2[t3]:=z
null t1:=x * 5 t3:=t1 + y 2 A x y 1 A y null x x null

38 Assignment 17—7.17 C语言和Java语言的数组声明和数组元素引用的语法形式同7.3.节讨论的不一样,例如float A[10][20]和A[i+1][j-1],并且每一维的下界都是0。若适应这种情况的赋值语句的文法如下: S --> L := E E --> E + E | (E ) | L L --> L [E ] | id a)重新设计7.3.2节数组元素的地址计算公式,以方便编译器产生数组元素地址计算的中间代码。不要忘记每一维的下界都是0 b)重新设计数组元素地址计算的翻译方案。只需写出产生式L --> L [E ] | id的翻译方案,但要能和7.3.3节中产生式S --> L := E和E --> E + E | (E ) | L的翻译方案衔接。若翻译方案中引入新的函数调用,要解释这些函数的含义

39 Assignment 17—7.17 a)重新设计7.3.2节数组元素的地址计算公式,以方便编译器产生数组元素地址计算的中间代码。不要忘记每一维的下界都是0 对于一个二维数组,A[i1] [i2]的地址计算公式: base + i1 * w1 + i2 * w2 w1 是存放一行元素的字节数(可以认为其是一个二维元素) w2 是存放行中一个元素的字节数(可以认为其是一个一维元素) 推广到多维: A[i1] [i2]…[ik]的地址计算公式: base + i1 * w1 + i2 * w2 +…+ ik * wk wk 是存放一个k 维元素的字节数

40 Assignment 17—7.17 b)重新设计数组元素地址计算的翻译方案。只需写出产生式L --> L [E ] | id的翻译方案,但要能和7.3.3节中产生式S --> L := E和E --> E + E | (E ) | L的翻译方案衔接。若翻译方案中引入新的函数调用,要解释这些函数的含义 L -> L1 [E] {L.array = L1.array; L.ndim = L1.ndim + 1; w = getwidth(L.array, L.ndim ); if (L.ndim == 1) begin L.offset = newtemp; emit (L.offset, ‘=’, E.place, ‘*’, w); end else begin t = newtemp; L.offset = newtemp; emit (t, ‘=’, E.place, ‘*’, w); emit (L.offset, ‘=’, L1.offset, ‘+’, t); end} L -> id {L.place = id.place; L.offset = null; L.ndim = 0; L.array = id.place;}

41 Assignment 18—7.8 表7.3的语法制导定义把B-> E1< E2翻译成一对语句 if E1.place< E2.place goto … goto ... 可以用一个语句 if E1.place>= E2.place goto... 来代替,当B是真时执行后继代码。修改表7.3的语法制导定义,使之产生这种性质的代码。

42 Assignment 18—7.8 需要修改 E→ E1 or E2 和 E→ id1 relop id2 两个产生式的语法制导定义。
E1.true = E.true ; E1.false = newlable ; E2.true = E.true ; E2.false = E.false ; E.code = E1.code || gen ( ‘goto’ E1.true )|| gen (E1.false ‘:’ ) || E2.code E→ id1 relop id2 E.code = gen ( ‘if’ id1.place conreop id2.place ‘goto’ E.false) conrelop 为原 relop 关系运算符的‘非’关系运算符。

43 Assignment 18—7.10 1)为什么会出现1条指令前有多个标号的情况
答: 编译器每次遇到可能会跳转到此处的地方会预留一个标记,当后面的代码需要跳转时又会在相应的地方添加标记,所以会有多条。 2)每个函数都有这样的标号.L1,它可能有什么作用?为什么本函数未引用它 答: L1 是预留以用来返回的,本函数没有return,所以没有用到L1

44 Assignment 19—7.9 为C语言程序 main(){ int i, j;
while ((i || j) && ( j>5 )){ i = j; } 的汇编代码中有关指令后加注释,判断是否确实短路计算

45 Assignment 19—7.9 主要关心跳转指令附近的代码,为以下代码进行注释 .L2:
cmpl $0, -4(%ebp) //计算i的bool值 jne .L //真转L6 cmpl $0, -8(%ebp) //计算j的bool值 jmp .L //(i || j)假则转L5 .p2align 4 , , 7 .L6: cmpl $5, -8(%ebp) //计算j>5的bool值 jg .L //真转L4 jmp .L //假转L5 .L5: jmp .L //(i || j)&&(j>5)假时转L3, 与while翻译有关 可看出是短路计算

46 Assignment 20—6.2 一个含有3文件的C程序如下 head.h: short int a = 10;
file1.c:  #include "head.h"  main(){} file2.c:  #include "head.h“ gcc file1.c file2.c 报错:multiple definition of ‘a’

47 Assignment 20—6.2 可以采取以下解决方案防止犯错 #ifndef __XX_H__ #define __XX_H__
#endif

48 Assignment 20—6.5 typedef struct a{ short i; short j; short k; }a;
typedef struct b{ long i; }b; main(){ printf("size of short, long, a and b = %d,%d,%d,%d\n", sizeof(short),sizeof(long),sizeof(a),sizeof(b)); } 已知short和long分别对齐2和8的倍数,为什么b的size为16 答:为方便数组计算时,每个元素能对其到该元素长度倍数,所以取最大元素倍数

49 Assignment 20—6.5 (但你也可以打开VS里面结构体成员对齐的设置,会有不一样的情况哦)

50 Assignment 20—6.6 下面是C语言两个函数f和g的概略(它们不再有其它的局部变量):
int f (int x) { int i; …return i +1; … } int g (int y) {int j; … f (j +1); … } 请按照教材上例6.5中图6.13的形式,画出函数g调用f,f的函数体正在执行时,活动记录栈的内容及相关信息,并按图6.12左侧箭头方式画出控制链。假定函数返回值是通过寄存器传递的。

51 Assignment 21—6.9 c函数定义如下 int f(int x, int* py,int** ppz){
**ppz+=1; *py+=2;x+=3; return x+*py+**ppz; } a为指向b指针,b为指向c指针,c为int,值为4 求f(c,b,a)返回值 答案:c+=1;c+=2;x+=3;(此时c=7,x=7) return x+c+c=21

52 Assignment 21—6.10 一个 C 语言程序如下: func ( i1, i2, i3) long i1, i2, i3;{
long j1, j2, j3; printf( "Addresses of i1, i2, i3 = %o, %o, %o \n", &i1, &i2, &i3); printf( "Addresses of j1, j2, j3 = %o, %o, %o \n", &j1, &j2, &j3); } main(){ long i1, i2, i3; func(i1, i2, i3); 该程序在 X86/Linux 机器上的运行结果如下: Addresses of i1, i2, i3 = , , Addresses of j1, j2, j3 = , , func 函数的 3个形式参数的地址依次升高,而 3 个局部变量的地址依次降低。 试说明为什么会有这个区别。注意,输出的数据是八进制的

53 Assignment 21—6.10 一个 C 语言程序如下: func ( i1, i2, i3) long i1, i2, i3;{
long j1, j2, j3; printf( "Addresses of i1, i2, i3 = %o, %o, %o \n", &i1, &i2, &i3); printf( "Addresses of j1, j2, j3 = %o, %o, %o \n", &j1, &j2, &j3); } main(){ long i1, i2, i3; func(i1, i2, i3); 答案:局部变量与参数虽然均保存在活动记录栈中,但保存位置不同,进栈次序也不同 实参进栈次序是i3, i2, i1,保存在栈底,栈向低地址增长,所以i3 i2 i1依次增大 局部变量j1, j2, j3在栈顶部,且依次进栈,所以j1, j2, j3地址逐渐减小

54 Assignment 21—6.13 int fact(i) | main() int i; | {
{ if(i==0) | printf("%d\n", fact(5)); return 1; | printf("%d\n", fact(5,10,15)); else | printf("%d\n", fact(5.0)); return i*fact(i-1); | printf("%d\n", fact()); } | } 该程序在X86/Linux机器上的运行结果如下: 120 1 Segmentation fault (core dumped)

55 Assignment 21—6.13 1)第二次调用为什么没有受参数过多的影响?
参数表达式逆序计算并进栈,fact取到了第一个参数,后面两个参数无空间保存,所以运行结果与第一次相同

56 Assignment 21—6.13 1)第二次调用为什么没有受参数过多的影响?
参数表达式逆序计算并进栈,fact取到了第一个参数,后面两个参数无空间保存,所以运行结果与第一次相同 2)第三次调用为什么结果变成1? 将5.0以二进制表示 取后四个字节,即是0,输出1

57 Assignment 21—6.13 1)第二次调用为什么没有受参数过多的影响?
参数表达式逆序计算并进栈,fact取到了第一个参数,后面两个参数无空间保存,所以运行结果与第一次相同 2)第三次调用为什么结果变成1? 将5.0以二进制表示 取后四个字节,即是0,输出1 3)第四次调用为什么会出现Segmentation fault? 由于没有提供参数,而main函数又无局部变量,fact把老ebp(控制链)(main的活动记录中保存的ebp)当成参数,它一定是一个很大的整数,使得活动记录栈溢出

58 Q&A 祝大家期末加油!

59 Q&A 祝大家期末加油!


Download ppt "习题课 编译原理与技术 计算机科学与技术学院 李诚."

Similar presentations


Ads by Google