编译原理课后答案4ppt课件.ppt_第1页
编译原理课后答案4ppt课件.ppt_第2页
编译原理课后答案4ppt课件.ppt_第3页
编译原理课后答案4ppt课件.ppt_第4页
编译原理课后答案4ppt课件.ppt_第5页
已阅读5页,还剩39页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

编译原理习题课(4),29.04.2020,1,6.1,使用Pascal的作用域规则,确定下面程序中用于名字a,b的每个出现的声明。程序输出整数1,2,3,4programa(inputoutput);procedureb(u,v,x,y:integer);vara:recorda,b:integerend;b:recordb,a:integerend;beginwithadobegina:=u;b:=vend;withbdobegina:=x;b:=yend;writeln(a.a,a.b,b.a,b.b)end;beginb(1,2,3,4)end.,2,6.1(续),withaarecorda:=uaa.ab:=vba.bwithbbrecorda:=xab.ab:=ybb.b,3,6.2,考虑下面的C程序main()char*cp1,*cp2;cp1=“12345”;cp2=“abcdefghij”;strcpy(cp1,cp2);printf(“cp1=%sncp2=%sn”,cp1,cp2);该程序经以前的某些C编译器编译后,运行结果为:cp1=abcdefghijcp2=ghij试分析为什么cp2被修改,4,6.2(续),C语言中,字符串会添加0作为串的结束符,因此,串”12345”存储为”123450”,而串”123450abc0”打印出来的只有12345常量区连续分配因而本题中”12345”和”abcdefghij”存储为123450abcdefghij0cp1cp2拷贝后结果为abcdefghij0fghij0cp1cp2现代编译器编译通过,执行时会出错。(GCC:段错误/VC非法访问),5,6.3,一个C程序如下:typedefstruct_acharc1;longI;charc2;doublef;a;typedefstruct_bcharc1;charc2;longl;doublef;b;main()printf(“Sizeofdouble,long,char=%d,%d,%dn”,sizeof(double),sizeof(long),sizeof(char);printf(“Sizeofa,b=%d,%dn”,sizeof(a),sizeof(b);该程序在SPARC/Solaris工作站上运行结果如下:Sizeofdouble,long,char=8,4,1Sizeofa,b=24,16试分析为什么,6,6.3(续),数据对齐:为了寻址方便A:charOXXXlongOOOOcharOXXXXXXXdoubleOOOOOOOOB:charOcharOXXlongOOOOdoubleOOOOOOOO可以用gccS命令查看编译后的汇编码VC下可以在debug模式下,菜单栏View-DebugWindows中Dissassenbly查看编译后的汇编码GCC:(GNU)3.2.2(RedHatLinux3.2.2-5)结果为20,16,7,6.3(续),#includestaticstruct_acharc1;longi;charc2;doublef;a=A,1,B,1.0;VC6下,Debug模式Memory窗口查看,GCC:(GNU)3.2.220030222(RedHatLinux3.2.2-5),|A|1|B|1.0|,8,6.4,下面给出一个C程序及其在X86/Linux下的编译结果,根据所生成的汇编程序来解释程序中4个变量的存储分配、作用域、生成期和置初始值方式的区别staticlongaa=10;shortbb=20;func()staticlongcc=30;shortdd=40;生成的汇编代码:,9,6.4(续),.filestatic.c“.version“01.01”gcc2_compiled:.data.align4.typeaa,object.sizeaa,4aa:.long10.globlbb.align2.typebb,object.sizebb,2bb:.value20.align4.typecc.2,object.sizecc.2,4,cc.2:.long30.text.align4.globlfunc.typefunc,functionfunc:pushl%ebpmovl%esp,%ebpsubl$4,%espmovw$40,-2(%ebp).L1:leaveret.Lfe1:.sizefunc,.Lfe1-func.identGCC:(GNU)egcs-2.91.6619990314/Linux(egcs-1.1.2release)”,10,6.4(续),.filestatic.c“.version“01.01”gcc2_compiled:.data.align4.typeaa,object.sizeaa,4aa:-aa分配在静态数据区,作用域为本文件,生存期为整个程序.long10aa静态置初值.globlbb-bb分配在静态数据区,作用域为全局,可以被其他文件引用,生存期为整个程序.align2.typebb,object.sizebb,2bb:.value20bb静态置初值.align4.typecc.2,object.sizecc.2,4,cc.2:-cc分配在静态数据区,作用域为本文件,生存期为整个程序。源程序中在函数内部,为防止重名,需要重命名为cc.2.long30cc静态置初值.text.align4.globlfunc.typefunc,functionfunc:pushl%ebpmovl%esp,%ebpsubl$4,%espmovw$40,-2(%ebp)-dd分配在栈上,生存期为func调用期,动态置初值.L1:leaveret.Lfe1:.sizefunc,.Lfe1-func.identGCC:(GNU)egcs-2.91.6619990314/Linux(egcs-1.1.2release)”,11,6.5,假定使用:(a)值调用;(b)引用调用;(c)值-结果调用;(d)换名调用。下面程序的结果分别是什么?programmain(input,output);vara,b:integer;procedurep(x,y,z:integer);beginy:=y+1;z:=z+x;end;begina:=2;b:=3;p(a+b,a,a);printa;end.,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,13,6.6,一个C程序如下:func(i1,i2,i3)longi1,i2,i3;longj1,j2,j3;printf(“Addressofi1i2i3=%o,%o,%on”,该程序在X86/Linux上运行结果为:Addressofi1,i2,i3=27777775460,27777775464,27777775470Addressofj1,j2,j3=27777775444,27777775440,27777775434从结果看func的3个形参地址逐渐升高,而3个局部变量地址逐渐降低。试说明为什么,14,6.6(续),C语言中,实参从右向左进栈,所以func(i1,i2,i3)按i3,i2,i1的顺序进栈而j1,j2,j3按声明的顺序分配,15,6.7,下面的C程序中,printf的调用仅含格式控制串,运行时输出3个参数,分析之main()printf(“%d%d%dn”);,16,6.7(续),C语言不做实参和形参个数类型是否一致的检查printf函数根据第一个参数格式控制列表,到栈中取参数本题中虽然只传了格式控制列表,但是printf函数分析格式控制列表,认为程序员还传了3个整型数,因此继续去栈中取3个参数,并输出之。所以得到了三个不可预知值得整数。,17,6.8,下面给出一个C程序及其在X86/Linux下的编译结果。从结果看,func的四个局部变量i1,j1,f1,e1的地址间隔和他们的类型一致,而形参i,j,f,e的地址间隔和他们的类型不一致,试分析原因func(i,j,f,e)shorti,j;floatf,e;shorti1,j1;floatf1,e1;printf(“Addressofi,j,f,e=%o,%o,%o,%on”,运行结果:Addressofi,j,f,e=35777772536,35777772542,35777772544,35777772554Addressofi1,j1,f1,e1=35777772426,35777772426,35777772424,35777772420,35777772414Sizeofshort,int,long,float,double=2,4,4,4,8,18,6.8(续),C语言为了不保证实参和形参类型一致,因此为了尽可能保证得到正确结果,编译器在整型和实型做实参时,将他们提升为long和double传递。但是函数内部取参数时,仍按照原来的类型去取,19,6.8(续),20,6.9,一个C程序func(c,l)charc;longl;func(c,l);在x86/Linux上编译生成的汇编代码如下,请说明char和long在参数传递和存储分配上的区别,21,6.9(续),.fileparameter.c“.version“01.01”gcc2_compiled.:.text.align4.globlfunc.typefunc,functionfunc:pushl%ebp-将老的基址指针压栈movl%esp,%ebp-将当前栈顶指针作为基址subl$4,%esp-分配空间movl8(%ebp),%eaxmovb%al,-1(%ebp),movl12(%ebp),%eaxpushl%eaxmovsbl-1(%ebp),%eaxpushl%eaxcallfuncaddl$8,%esp.L1:leaveret.Lfe1:.sizefunc,.Lfe1-func.identGCC:(GNU)egcs-2.91.6619990314/Linux(egcs-1.1.2release)”,22,6.9(续)SomeATvarf,n:integer;functionfactor(n:integer):integer;beginifn=0thenfactor:=1elsefactor:=n*(factor(n-1)end;beginn:=5;f:=factor(n);write(f)end.,27,6.11(续),1,2,2,28,6.12,在下面假想的程序中,第(11)行语句f:=a调用函数a,a传递函数addm作为返回值.(a)画出该程序执行的活动树.(b)假定非局部名字使用静态作用域,为什么该程序在栈式分配情况下不能正确工作?(c)在堆分配策略下,该程序的输出是什么?,29,6.12(续),programret(input,output);varf:function(integer):integer;functiona:function(integer):integervarm:integer;functionaddm(n:integer):integerbeginreturnm+nend;beginm:=0;returnaddmend;procedureb(g:function(integer):integer);beginwriteln(g(2)end;beginf:=a;b(f)end,30,6.12(续),活动树如右图在执行addm时,只有ret,b和addm的活动记录,a的已释放。如果是静态作用域,n对应的是第(4)行中的m,他处于a的活动记录中,而此时a的已释放。堆分配结果是2,ret,a,b,addm,31,6.13,为什么C语言允许函数类型(的指针)作为函数的返回值类型,而Pascal语言却不允许?,32,6.13(续),参考6.12课本P202,33,6.14,一个C语言程序如下:intn;intf(intg)intm;m=n;if(m=0)return1;elsen=n-1;returnm*f(n);main()n=5;printf(“%dfactorialis%dn”,n,f(n);该程序的运行结果不是我们所期望的5factorialis120而是0factorialis120试说明原因.,34,6.14(续),参数逆序进栈,因此,f(n)先被执行,而f(n)执行结束时,n值已经被改写为0,此时再将n值压栈,因而输出“0factorialis120”,35,6.15,下面程序在SPARC/SUN工作站上运行时陷入死循环,试说明原因.如果将第7行的long*p改成short*p,并且将第22行longk改成shortk后,loop中的循环体执行一次便停止了.试说明原因.main()addr();loop();long*p;loop()longi,j;j=0;for(i=0;i10;i+)(*p)-;j+;addr()longk;k=0;p=,36,6.15(续),程序运行时陷入死循环的原因是由于指向分配给i的存储单元引起的。循环体执行一次便停止时由于p指向分配给i的高位字节引起的。C语言的实现是采用栈式分配,使得函数addr和函数loop的活动记录先后从同样的存储单元开始分配,从而长整数k和i先后分配在同样的存储单元,因此p指向分配给i的存储单元。SPARC/SUN工作站上整数的存放方式是低地址放高位字节,而高地址放低位字节。另外,活动记录栈是从高地址向低地址方向增长。当k改为短整型且p改为短整型指针后,那么p指向由i的两个低位字节组成的短整数。执行(*p)-使得这两个字节构成的短整数等于-1。而从整个4个字节看时,是65535,远大于10。所以循环体执行一次便停止。,37,6.16,一个C语言程序main()func();printf(“Returnfromfuncn”);func()chars4;strcpy(s,”12345678”);printf(“%sn”,s);在X86/Linux操作系统上的运行结果如下:12345678ReturnfromfuncSegmentationfault(coredumped)试分析为什么会出现这样的运行错误.,38,6.16(续),数组越界访问。出现短错误,说明控制链被破坏func可以返回main说明func的返回地址没有被破坏,而main不能返回,说明main的返回地址被破坏本题RedHatLinux3.2.2/GCC:(GNU)3.2.2下结果为12345678段错误main返回地址被破坏,39,6.17,一个过程作为实在参数传递,它被激活时的计算环境有三种可能的规定.第一种是使用词法环境,即该过程激活

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论