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

Slides:



Advertisements
Similar presentations
C语言程序设计 主讲教师 :张群燕 电话:
Advertisements

电子成绩单项目实现.
编 译 原 理 指导教师:杨建国 二零一零年三月.
编译原理第二次习题课 王静.
“八皇后”问题 崔萌萌 吕金华.
Memory Pool ACM Yanqing Peng.
第一章 C语言概述 计算机公共教学部.
编译原理上机实习
第六章 中间代码生成 赵建华 南京大学计算机系.
第4章 数组 数组是由一定数目的同类元素顺序排列而成的结构类型数据 一个数组在内存占有一片连续的存储区域 数组名是存储空间的首地址
专题研讨课二: 数组在解决复杂问题中的作用
第9章 运行时的存储组织 重点:符号表的内容、组织,过程调用实现, 难点:参数传递,过程说明语句代码结构,
编译原理与技术 中间代码生成 2018/9/17 《编译原理与技术》讲义.
組員:蔡惠雅 494D0032 楊雅惠494B0079 蔡騏鴻 葉時宇 余建霖495B0002 陳瑛淑495B0021
第4章 鏈結串列(Linked Lists) 4-1 動態記憶體配置-(6) 4-2 鏈結串列的基礎-(7)
Linked List Operations
7 Intermediate Representation
C语言程序设计 第十二章 位运算.
主讲教师:吴琼 微信群:C语言2016 QQ群: 密码scu2016 昵称:“真名+学号”
编译原理与技术 第7章 中间代码生成 3学时.
补充内容 结构体 概述 定义结构体类型和定义结构体变量 结构体变量的引用 结构体变量的初始化 指针与结构体 用typedef定义类型的别名.
编译原理与技术 类型检查 2018/11/21 《编译原理与技术》-类型检查.
Chap 10 函数与程序结构 10.1 函数的组织 10.2 递归函数 10.3 宏定义 10.4 编译预处理.
结构体和共用体 2 梁春燕 华电信息管理教研室.
第六章 运行时存储空间的组织和管理 术语 本章内容 讨论一个活动记录中的数据布局 程序执行过程中,所有活动记录的组织方式 过程的活动
6 下标变量的中间代码生成 下标:数组;变量: 下标变量:即数组分量: 简单变量:id,其地址可查到;
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
程序设计期末复习 黎金宁
第三章 C++中的C 面向对象程序设计(C++).
第 6 章 函式.
程序设计专题一 结构化程序设计与递归函数 主讲教师: 刘新国.
第3章 變數、常數與資料型態 3-1 C語言的識別字 3-2 變數的宣告與初值 3-3 指定敘述 3-4 C語言的資料型態
第2章 线性表 线性表抽象数据类型 顺序表 主要知识点 单链表 循环单链表 循环双向链表 静态链表 设计举例.
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
第7章 编译预处理 本章要求: 本章重点: 本章难点: 掌握用#define定义无参数宏和带有参数宏定义和调用方法;
6 使用者函數 6.1 函數定義 宣告函數 呼叫函數 呼叫多個函數 6-6
7.4 布尔表达式和控制流语句 布尔表达式有两个基本目的 计算逻辑值 c = (a > b) && a > 1
明解C++教學手冊 柴田望洋 博士 著 書號:PG20269
中间代码生成.
計數式重複敘述 for 迴圈 P
$10 可空类型.
语义分析概述 符号表 第六章 语义分析.
第九章 预处理命令.
第1章 概述 本章要点: C语言程序结构和特点 C语言程序的基本符号与关键字 C语言程序的编辑及运行 学习方法建议:
编译原理实践 11.语义分析与代码生成.
C语言概述 第一章.
第1讲 C语言基础 要求: (1) C程序的组成 (2) C语言的标识符是如何定义的。 (3) C语言有哪些基本数据类型?各种基本数
C语言大学实用教程 第5章 函数与程序结构 西南财经大学经济信息工程学院 刘家芬
习题课
第3 语言翻译问题 [学习目标]:学习和掌握语言的语法的基本概念和基本要素,理解翻译的步骤;学习和掌握BNF文法。
7.1 C程序的结构 7.2 作用域和作用域规则 7.3 存储属性和生存期 7.4 变量的初始化
第十四章 若干深入问题和C独有的特性 作业: 函数指针 函数作参数 函数副作用 运算 语句 位段 存储类别 编译预处理
C程序设计.
第5章 函 数.
C qsort.
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
第二章 Java语法基础.
第7章 程序的结构 四、生存期与存储属性 五、extern关键字与外部连接属性 六、static关键字与内部连接属性.
第二章 类型、对象、运算符和表达式.
#include <iostream.h>
第二章 Java基本语法 讲师:复凡.
本节内容 指针类型.
第4章 鏈結串列(Linked Lists) 4-1 動態記憶體配置-(6) 4-2 鏈結串列的基礎-(7)
本章主題 C++的程式結構 資料型態與宣告 算術運算 簡易的輸入輸出指令 程式編譯(Compile)的過程與原理.
第2章 Java语言基础.
基本資料型態 變數與常數 運算子 基本的資料處理 授課:ANT 日期:2014/03/03.
第三章 流程控制 程序的运行流程 选择结构语句 循环结构语句 主讲:李祥 时间:2015年10月.
第六章 复合数据类型 指针的声明与使用 数组的声明与使用 指针与数组的相互引用 字符串及相关库函数 new与delete
本节内容 指针类型 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
函式庫補充資料 1.
Presentation transcript:

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

Assignment 11 4.15

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

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

Assignment 12 附加题

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

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

Assignment 13 5.4 5.5 5.6

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

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

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的类型写出类型表达式。

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

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

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

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

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

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

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

Assignment 15 5.12 5.13 5.9b 5.21 7.2

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

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;}

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 请你对该程序略作修改,使 得该警告错误能消失,并且 不改变程序的结果。

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的第四个形 式参数类型与函数调用 的传参类型不一致

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);

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 函数调用中第四个参数 的类型

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

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

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

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

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}

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} 类型唯一

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} 类型唯一

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

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

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 为综合属性,记录该过程所分配的空间

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

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

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的翻译方案衔接。若翻译方案中引入新的函数调用,要解释这些函数的含义

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 维元素的字节数

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;}

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的语法制导定义,使之产生这种性质的代码。

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 关系运算符的‘非’关系运算符。

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

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

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

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’

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

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 答:为方便数组计算时,每个元素能对其到该元素长度倍数,所以取最大元素倍数

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

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左侧箭头方式画出控制链。假定函数返回值是通过寄存器传递的。

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

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 = 27777775460, 27777775464, 27777775470 Addresses of j1, j2, j3 = 27777775444, 27777775440, 27777775434 func 函数的 3个形式参数的地址依次升高,而 3 个局部变量的地址依次降低。 试说明为什么会有这个区别。注意,输出的数据是八进制的

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地址逐渐减小

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)

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

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

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

Q&A 祝大家期末加油!

Q&A 祝大家期末加油!