资源描述
单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,*,*,编译原理习题课(4),栾 俊,11/28/2024,11/28/2024,1,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.,11/28/2024,2,6.1(续),with aarecorda:=uaa.ab:=vba.bwith bbrecorda:=xab.ab:=ybb.b,11/28/2024,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 试分析为什么cp2被修改,11/28/2024,4,6.2(续),C语言中,字符串会添加0作为串的结束符,因此,串”12345”存储为”123450”,而串”123450abc0”打印出来的只有12345,常量区连续分配,因而本题中”12345”和”abcdefghij”存储为1 2 3 4 5 0 a b c d e f g h i j 0cp1 cp2拷贝后结果为a b c d e f g h i j 0 f g h i j 0cp1 cp2,现代编译器编译通过,执行时会出错。(GCC:段错误/VC 非法访问),11/28/2024,5,6.3,一个C程序如下:typedef struct _a char c1;long I;char c2;double f;a;typedef struct _b char c1;char c2;long l;double f;b;main()printf(“Size of double,long,char=%d,%d,%dn”,sizeof(double),sizeof(long),sizeof(char);printf(“Size of a,b=%d,%dn”,sizeof(a),sizeof(b);该程序在SPARC/Solaris工作站上运行结果如下:Size of double,long,char=8,4,1Size of a,b=24,16试分析为什么,11/28/2024,6,6.3(续),数据对齐:为了寻址方便,A:charOXXXlongOOOOcharOXXX XXXXdoubleOOOO OOOO,B:charOcharOXXlongOOOO doubleOOOO OOOO,可以用gcc S命令查看编译后的汇编码VC下可以在debug模式下,菜单栏View-Debug Windows中 Dissassenbly查看编译后的汇编码,GCC:(GNU)3.2.2(Red Hat Linux 3.2.2-5)结果为20,16,11/28/2024,7,6.3(续),#include static struct _achar c1;long i;char c2;double f;a =A,1,B,1.0;,VC6下,Debug模式Memory窗口查看,GCC:(GNU)3.2.2 20030222(Red Hat Linux 3.2.2-5),|A|1|B|1.0|,11/28/2024,8,6.4,下面给出一个C程序及其在X86/Linux下的编译结果,根据所生成的汇编程序来解释程序中4个变量的存储分配、作用域、生成期和置初始值方式的区别static long aa=10;short bb=20;func()static long cc=30;short dd=40;生成的汇编代码:,11/28/2024,9,6.4(续),.file static.c“,.version“01.01”,gcc2_compiled:,.data,.align 4,.type aa,object,.size aa,4,aa:,.long 10,.globl bb,.align 2,.type bb,object,.size bb,2,bb:,.value 20,.align 4,.type cc.2,object,.size cc.2,4,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,.Lfe1:,.size func,.Lfe1-func,.ident GCC:(GNU)egcs-2.91.66 19990314/Linux(egcs-1.1.2 release)”,11/28/2024,10,6.4(续),.file static.c“,.version“01.01”,gcc2_compiled:,.data,.align 4,.type aa,object,.size aa,4,aa:,-aa分配在静态数据区,作用域为本文件,生存期为整个程序,.long 10,aa静态置初值,.globl bb,-bb分配在静态数据区,作用域为全局,可以被其他文件引用,生存期为整个程序,.align 2,.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静态置初值,.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)”,11/28/2024,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.,11/28/2024,12,6.5(续),值调用x:=5;y:=2;z:=2;y:=y+1;z:=z+x;对形参的调用不改变实参的值,结果a为2,引用调用t:=a+b;a=a+1;a=a+t;结果a为8,值-结果调用t:=a+b;x:=t;y:=a;z:=ay:=y+1;z:=z+x;t:=x;a:=y;a:=z;结果为7,换名调用a:=a+1;a:=a+(a+b);结果为9,11/28/2024,13,6.6,一个C程序如下:func(i1,i2,i3)long i1,i2,i3;long j1,j2,j3;printf(“Address of i1 i2 i3=%o,%o,%on”,该程序在X86/Linux上运行结果为:Address of i1,i2,i3=27777775460,27777775464,27777775470Address of j1,j2,j3=27777775444,27777775440,27777775434从结果看func的3个形参地址逐渐升高,而3个局部变量地址逐渐降低。试说明为什么,11/28/2024,14,6.6(续),C语言中,实参从右向左进栈,所以func(i1,i2,i3)按i3,i2,i1的顺序进栈,而j1,j2,j3按声明的顺序分配,11/28/2024,15,6.7,下面的C程序中,printf的调用仅含格式控制串,运行时输出3个参数,分析之main()printf(“%d%d%dn”);,11/28/2024,16,6.7(续),C语言不做实参和形参个数类型是否一致的检查,printf函数根据第一个参数格式控制列表,到栈中取参数,本题中虽然只传了格式控制列表,但是printf函数分析格式控制列表,认为程序员还传了3个整型数,因此继续去栈中取3个参数,并输出之。,所以得到了三个不可预知值得整数。,11/28/2024,17,6.8,下面给出一个C程序及其在X86/Linux下的编译结果。从结果看,func的四个局部变量i1,j1,f1,e1的地址间隔和他们的类型一致,而形参i,j,f,e的地址间隔和他们的类型不一致,试分析原因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,%on”,运行结果:Address of i,j,f,e=35777772536,35777772542,35777772544,35777772554Address of i1,j1,f1,e1=35777772426,35777772426,35777772424,35777772420,35777772414Size of short,int,long,float,double=2,4,4,4,8,11/28/2024,18,6.8(续),C语言为了不保证实参和形参类型一致,因此为了尽可能保证得到正确结果,编译器在整型和实型做实参时,将他们提升为long和double传递。,但是函数内部取参数时,仍按照原来的类型去取,11/28/2024,19,6.8(续),main传参数时,提升数据类型,func取参数时,按原来的数据类型,i:short-int,4字节,j:short-int,4字节,f:long-double,8字节,e:long-double,8字节,i:short 2字节,j:short 2字节,f:long 4字节,e:long 4字节,11/28/2024,20,6.9,一个C程序func(c,l)char c;long l;func(c,l);在x86/Linux上编译生成的汇编代码如下,请说明char和long在参数传递和存储分配上的区别,11/28/2024,21,6.9(续),.file parameter.c“,.version“01.01”,gcc2_compiled.:,.text,.align 4,.globl func,.type func,function,func:,pushl%ebp -将老的基址指针压栈,movl%esp,%ebp -将当前栈顶指针作为基址,subl$4,%esp -分配空间,movl 8(%ebp),%eax,movb%al,-1(%ebp),movl 12(%ebp),%eax,pushl%eax,movsbl-1(%ebp),%eax pushl%eax,call func,addl$8,%esp,.L1:,leave,ret,.Lfe1:,
展开阅读全文