编译原理 陈意云 课后答案
gcc2_compiled:
.data
.align 4
.type aa,@object
.size aa,4
aa:
--aa分配在静态数据区,作
用域为本文件,生存期为整个程序
.long 10 –aa静态置初值
.globl bb
--bb分配在静态数据区,作
用域为全局,可以被其他文件引用,
生存期为整个程序
.align 2
7
6.3 (续)
• #include <stdio.h> static struct _a{ char c1; long i; char c2; double f; } a = {'A', 1, 'B', 1.0};
• VC6下,Debug模式Memory窗口查看
|A
|1
|B
|1.0
|
• GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)
.text .align 4 .globl func .type func,@function func: pushl %ebp movl %esp, %ebp subl $4, %esp movw $40, -2(%ebp) --dd分配在栈上,生存期为 func调用期,动态置初值 .L1: leave ret .Lfe1: .size func,.Lfe1-func .ident "GCC: (GNU) egcs-2.91.66 19990314/Linux(egcs-1.1.2 release)”
.type bb,@object
.size bb,2
bb:
.value 20 –bb静态置初值
.align 4
.type cc.2,@object
.size cc.2,4
cc.2: --cc分配在静态数据区,作用域为本文件, 生存期为整个程序。源程序中在函数内部,为防止 重名,需要重命名为cc.2
.long 30 –cc静态置初值
12.07.2020
luanj@
14
6.6 (续)
• C语言中,实参从右向左进栈,所以func(i1, i2, i3)按i3,i2,i1的顺序进栈
• 而j1,j2,j3按声明的顺序分配
12.07.2020
luanj@
15
6.7
• 下面的C程序中,printf的调用仅含格式控制 串,运行时输出3个参数,分析之 main(){ printf(“%d %d %d\n”); }
12.07.2020
luanj@
2
6.1 (续)
• with a a:=u b:=v with b a:=x b:=y
a—record a—a.a b—a.b b—record a—b.a b—b.b
12.07.2020
luanj@
3
6.2
• 考虑下面的C程序 main(){ char * cp1, * cp2; cp1 = “12345”; cp2 = “abcdefghij”; strcpy(cp1, cp2); printf(“cp1 = %s \n cp2 = %s \n”, cp1, cp2); }
该程序经以前的某些C编译器编译后,运行结果为: cp1 = abcdefghij cp2 = ghij
• 所以得到了三个不可预知值得整数。
12.07.2020
luanj@
17
6.8
• 下面给出一个C程序及其在X86/Linux下的编译结果。从结果看,func的四个局部变量i1, j1,f1,e1的地址间隔和他们的类型一致,而形参i,j,f,e的地址间隔和他们的类型 不一致,试分析原因
12.07.2020
luanj@
8
6.4
• 下面给出一个C程序及其在X86/Linux下的编译结 果,根据所生成的汇编程序来解释程序中4个变量 的存储分配、作用域、生成期和置初始值方式的 区别
static long aa = 10;
short bb = 20;
func(){
12.07.2020
cc.2: .long 30 .text .align 4
.globl func .type func,@function
func: pushl %ebp movl %esp, %ebp subl $4, %esp movw $40, -2(%ebp)
.L1: leave ret
func(i, j, f, e) short i, j; float f, e; {
short i1, j1; float f1, e1; printf(“Address of i, j, f,e = %o,%o,%o,%o\n”, &i, &j, &f, &e); printf(“Address of i1,j1,f1,e1 = %o,%o,%o,%o\n”, &i1, &j1, &f1, &e1); printf(“Address of short,int,long,float,double = %d,%d,%d,%d,%d\n”, sizeof(short), sizeof(int), sizeof(long), sizeof(float), sizeof(double)); } main(){ short i, j; float f, e; func(i, j, f, e); } 运行结果:
12.07.2020
luanj@
11
6.5
• 假定使用:(a)值调用;(b)引用调用;(c)值-结果调用;(d)换名 调用。下面程序的结果分别是什么?
program main(input, output); var a, b : integer; procedure p(x, y, z : integer); begin y := y + 1; z := z + x; end; begin a := 2; b := 3; p(a + b, a, a); print a; end.
• 换名调用 a := a+1; a := a+(a+b);
对形参的调用不改变实参的值,结果a 结果a为8 结果为7 结果为9
12.07.2020
luanj@
13
6.6
• 一个C程序如下: func(i1, i2, i3) long i1, i2, i3; { long j1, j2, j3; printf(“Address of i1 i2 i3 = %o,%o,%o\n”, &i1, &i2, &i3); printf(“Address of j1 j2 j3 = %o,%o,%o\n”, &j1, &j2, &j3); } main(){ long i1, i2, i3; func(i1, i2, i3); } 该程序在X86/Linux上运行结果为: Address of i1, i2, i3 = 27777775460,27777775464,27777775470 Address of j1, j2, j3 = 27777775444,27777775440,27777775434 从结果看func的3个形参地址逐渐升高,而3个局部变量地址逐渐降低。 试说明为什么
.Lfe1: .size func,.Lfe1-func .ident "GCC: (GNU) egcs-2.91.66
19990314/Linux(egcs-1.1.2 release)”
luanj@
10
6.4 (续)
.file "static.c“
.version “01.01”
12.07.2020
luanj@
6
6.3 (续)
• 数据对齐:为了寻址方便
• A: char long char
OXXX OOOO OXXX XXXX
double
OOOO OOOO
• B: char char long double
O OXX OOOO OOOO OOOO
12.07.2020
luanj@
12
6.5 (续)
• 值调用 x := 5; y := 2; z := 2; y := y + 1; z := z + x; 为2
• 引用调用 t := a + b; a = a + 1; a = a + t;
• 值-结果调用 t:=a+b; x:=t;y:=a;z:=a y:=y+1;z:=z+x; t:=x;a:=y;a:=z;
编译原理习题课(4)
栾俊 luanj@
12.07.2020
6.1
• 使用Pascal的作用域规则,确定下面程序中用于名字a,b 的每个出现的声明。程序输出整数1,2,3,4
program a (input output); procedure b (u, v, x, y : integer); var a : record a, b : integer end; b : record b, a : integer end; begin with a do begin a := u; b := v end; with b do begin a := x; b := y end; writeln (a.a, a.b, b.a, b.b) end; begin b(1, 2, 3, 4) end.
cp1
cp2
拷贝后结果为
a b c d e f g h i j \0 f g h i j \0
cp1
cp2
• 现代编译器编译通过,执行时会出错。(GCC: 段错误 / VC 非法访问)