【编译原理和技术试卷】2010
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
中国科学技术大学
2009-2010学年第二学期考试试卷(A)考试科目:编译原理和技术得分:
学生所在系:姓名:学号:
1、(15分)从教材第2章的习题中找出一个正规式,
依据教材上提供的方法确认该DFA是最简的DFA。
a
2、(10分)下面是类型表达式的语法:
type →integer | boolean | array[num] of type |
field_list→id : type | id : type ; field_list
若规定:在记录类型中不能出现数组类型(包括不能出现数组的指针类型)。请重新设计一个文法,把该约束体现在文法中,即它和上述文法的区别就是所定义的语言满足这个约束。
3、(10分)根据教材3.3.3节的递归下降预测分析方法以及图3.8的方式,给第2题题目(而不是你答案)中的非终结符field_list写一个递归过程(若需要变换文法,则先变换文法)。
4、(10分)为第2题题目的文法写一个翻译方案,若类型表达式不满足该题所规定的约束则报告错误。注:第2题通过重新设计文法,本题通过静态检查,来达到同样的目的。
5、(10分)
(1)Java语言的编译器通常把数组分配在堆上。Java数组一般不能静态确定大小应该不是将它分配在堆上的原因,因为教材上图6.12给出了一种将不能静态确定大小的数组动态地分配在活动记录栈上的方法。Java数组一般不能分配在活动记录栈上的原因是什么?
(2)将数组分配在活动记录栈上和分配在堆上给程序运行带来什么区别?
6、(5分)有人认为,下面C程序中结构体类型record的定义方式可用来动态生成其中a 数组的大小不一样的结构体,以适应某些编程场合的需要。你认为这样的程序能够通过C 编译器的类型检查吗?请说明理由。
#include
typedef struct {double r; int n; float a[];} record;
main() {
record * p;
p = malloc(sizeof(record) + sizeof(float) * 5);
p->n = 5; p->a[4] = 100.0; …
}
7、(15分)对于教材上P172例6.4的程序(见下面C代码),GCC编译器(版本:(GNU) 4.2.3
(Debian 4.2.3-5))所生成的优化代码(见下面中间的汇编代码)同书上给出的代码(见下面左边的汇编代码)略有不同。请指出该较新版本编译器完成的优化(或采用的不同代码生成策略)。给一点提示:若把调用语句改成func(j, j),所生成的优化代码和下面中间的汇编代码的区别见下面右边的说明。
func(i) long i; {
long j; j=i-1; func(j);
}
func: | func: |
pushl %ebp | pushl %ebp |
movl %esp, %ebp | movl %esp, %ebp |
subl $4, %esp | subl $4, %esp | 该指令改成subl $8, %esp
movl 8(%ebp), %edx | movl 8(%ebp), %eax |
decl %edx | subl $1, %eax | 在该指令和下条指令间
movl %edx, -4(%ebp) | movl %eax, (%esp) | 增加movl %eax, 4(%esp)
movl -4(%ebp), %eax | call func |
pushl %eax | leave |
call func | ret |
addl $4, %esp |
.L1: |
leave |
ret |
8、(5分)请按教材上图12.4的方式给出第375页中类Point的对象表示。
9、(10分)下面左右两边是一个C程序的两个源文件的内容:
#include
char *p = “0123456789”; | char p[10];
f () { | main () {
printf (“%s\n”, p); | f (); p[1] = …1‟; f ();
} | }
运行目标程序所得到的结果如下(编译器是GCC: (GNU) 4.2.3 (Debian 4.2.3-5) ):0123456789
Segmentation fault
请描述该目标程序各数据区的内容,并回答为什么运行时会出现Segmentation fault。
10、(10分)下面的C程序分别经非优化编译和2级以上(含2级)的优化编译后,生成的两个目标程序运行时的表现不同(编译器是GCC: (GNU) 4.2.3 (Debian 4.2.3-5))。请回答它们运行时的表现有何不同,并说明原因。
int f(int g( ) ) {
return g(g);
}
main() {
f(f);
}
2009-2010学年第二学期
编译原理和技术参考答案(A)
1、该DFA接受的是正规式(a|b)*a(a|b)所表示的语言。
该DFA的接受状态集合是{2, 3},非接受状态集是{0, 1}。由于状态2和3的a转换分别到状态2和1,并且状态2和1分处于两个集合,因此集合{2, 3}要分成两个子集;基于同样的道理,集合{0, 1}也要分成两个子集。所以这是最简DFA。
2、type →integer | boolean | array[num] of type | record field_list end | ↑type
field_list→id : field_type | id : field_type ; field_list
field_type→integer | boolean |record field_list end | ↑field_type
3、按习题3.7提供的方式修改文法:
field_list→id : type [ ; field_list ]
递归过程如下:
void field_list() {
match(id); match(…:‟); type();
if(lookahead == …;‟){
match(…;‟); field_list();
}
}
4、给非终结符type一个综合属性arr,若type含有数组类型则arr为真,否则为假。
type →integer{ type.arr = false; }
type →boolean{ type.arr = false; }
type →array[num] of type1 { type.arr = true; }
type →↑type1 { type.arr = type1.arr; }
type →record field_list end { type.arr = false; }
field_list→id : type { if (type.arr) print(“error”); }
field_list→id : type ; field_list { if (type.arr) print(“error”); }
5、(1)Java的数组是一种引用类型,数组变量之间的赋值b = a会导致b也指向a所指向的数组。由此,在一个方法m中,不管是用像int arr[] = {1, 2, 3, 4}这样的方式还是用像int arr[] = new int[4]这样的方式创建数组,在m的执行结束后该数组都还有可能被引用,因此不能分配在活动记录栈上。
(2)分配在活动记录上:及时回收数组占用的空间并且回收开销小。分配在堆上:在垃圾收集器工作时才回收并且回收开销大。
6、这是一种编程技巧,编译器不会认为这其中有什么类型错误。对于含malloc调用的那个语句,只要赋值号的左边是指针类型的左值表达式就可以了。对于赋值p->a[4] = 100.0,由于C编译器不进行下标表达式是否越界的检查,因此也不会报告错误。
7、书上所用编译器:一般只为局部变量分配空间。调用函数前,用若干次pushl指令