編譯原理陳意云課后答案專題培訓課件_第1頁
編譯原理陳意云課后答案專題培訓課件_第2頁
編譯原理陳意云課后答案專題培訓課件_第3頁
編譯原理陳意云課后答案專題培訓課件_第4頁
編譯原理陳意云課后答案專題培訓課件_第5頁
已閱讀5頁,還剩38頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

4編譯原理陳意云課后答案46.1使用Pascal的作用域規則,確定下面程序中用于名字a,b的每個出現的聲明。程序輸出整數1,2,3,4 programa(inputoutput);

procedureb(u,v,x,y:integer);

vara:recorda,b:integerend;

b:recordb,a:integerend;

begin

withadobegina:=u;b:=vend;

withbdobegina:=x;b:=yend;

writeln(a.a,a.b,b.a,b.b)

end;

begin

b(1,2,3,4)

end.7/9/20232luanj@6.1(續)witha a—record

a:=u a—a.a

b:=v b—a.b

withb b—record

a:=x a—b.a

b:=y b—b.b7/9/20233luanj@6.2考慮下面的C程序

main(){

char*cp1,*cp2;

cp1=“12345”;

cp2=“abcdefghij”;

strcpy(cp1,cp2);

printf(“cp1=%s\ncp2=%s\n”,cp1,cp2);

}

該程序經以前的某些C編譯器編譯后,運行結果為:

cp1=abcdefghij

cp2=ghij

試分析為什么cp2被修改7/9/20234luanj@6.2(續)C語言中,字符串會添加‘\0’作為串的結束符,因此,串”12345”存儲為”12345\0”,而串”12345\0abc\0”打印出來的只有12345常量區連續分配因而本題中”12345”和”abcdefghij”存儲為

12345\0abcdefghij\0

cp1cp2

拷貝后結果為

abcdefghij\0fghij\0

cp1cp2現代編譯器編譯通過,執行時會出錯。(GCC:段錯誤/VC非法訪問)7/9/20235luanj@6.3一個C程序如下:

typedefstruct_a{

charc1;

longI;

charc2;

doublef;

}a;

typedefstruct_b{

charc1;

charc2;

longl;

doublef;

}b;

main(){

printf(“Sizeofdouble,long,char=%d,%d,%d\n”,sizeof(double),sizeof(long),sizeof(char));

printf(“Sizeofa,b=%d,%d\n”,sizeof(a),sizeof(b));

}

該程序在SPARC/Solaris工作站上運行結果如下:

Sizeofdouble,long,char=8,4,1

Sizeofa,b=24,16

試分析為什么7/9/20236luanj@6.3(續)數據對齊:為了尋址方便A:

char OXXX

long OOOO

char OXXXXXXX

double OOOOOOOOB:

char O

char OXX

long OOOO

double OOOOOOOO可以用gcc–S命令查看編譯后的匯編碼

VC下可以在debug模式下,菜單欄View->DebugWindows中Dissassenbly查看編譯后的匯編碼GCC:(GNU)3.2.2(RedHatLinux3.2.2-5)結果為20,167/9/20237luanj@6.3(續)#include<stdio.h>

staticstruct_a{

charc1;

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|7/9/20238luanj@6.4下面給出一個C程序及其在X86/Linux下的編譯結果,根據所生成的匯編程序來解釋程序中4個變量的存儲分配、作用域、生成期和置初始值方式的區別

staticlongaa=10;

shortbb=20;

func(){

staticlongcc=30;

shortdd=40;

}

生成的匯編代碼:7/9/20239luanj@6.4(續).file"static.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,4cc.2:.long30.text .align4.globlfunc.typefunc,@functionfunc:pushl%ebpmovl%esp,%ebpsubl$4,%espmovw$40,-2(%ebp).L1:leaveret.Lfe1:.sizefunc,.Lfe1-func.ident"GCC:(GNU)egcs-2.91.6619990314/Linux(egcs-1.1.2release)”7/9/202310luanj@6.4(續).file"static.c“.version“01.01”gcc2_compiled:.data.align4.typeaa,@object.sizeaa,4aa: --aa分配在靜態數據區,作用域為本文件,生存期為整個程序.long10–aa靜態置初值.globlbb--bb分配在靜態數據區,作用域為全局,可以被其他文件引用,生存期為整個程序.align2.typebb,@object.sizebb,2bb:.value20–bb靜態置初值.align4 .typecc.2,@object.sizecc.2,4cc.2:--cc分配在靜態數據區,作用域為本文件,生存期為整個程序。源程序中在函數內部,為防止重名,需要重命名為cc.2.long30–cc靜態置初值.text .align4.globlfunc.typefunc,@functionfunc:pushl%ebpmovl%esp,%ebpsubl$4,%espmovw$40,-2(%ebp)--dd分配在棧上,生存期為func調用期,動態置初值.L1:leaveret.Lfe1:.sizefunc,.Lfe1-func.ident"GCC:(GNU)egcs-2.91.6619990314/Linux(egcs-1.1.2release)”7/9/202311luanj@6.5假定使用:(a)值調用;(b)引用調用;(c)值-結果調用;(d)換名調用。下面程序的結果分別是什么?

programmain(input,output);

vara,b:integer;

procedurep(x,y,z:integer);

begin

y:=y+1;

z:=z+x;

end;

begin

a:=2;

b:=3;

p(a+b,a,a);

printa;

end.7/9/202312luanj@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:=a

y:=y+1;z:=z+x;

t:=x;a:=y;a:=z; 結果為7換名調用

a:=a+1;

a:=a+(a+b); 結果為97/9/202313luanj@6.6一個C程序如下:

func(i1,i2,i3)

longi1,i2,i3;

{

longj1,j2,j3;

printf(“Addressofi1i2i3=%o,%o,%o\n”,&i1,&i2,&i3);

printf(“Addressofj1j2j3=%o,%o,%o\n”,&j1,&j2,&j3);

}

main(){

longi1,i2,i3;

func(i1,i2,i3);

}

該程序在X86/Linux上運行結果為:

Addressofi1,i2,i3=27777775460,27777775464,27777775470

Addressofj1,j2,j3=27777775444,27777775440,27777775434

從結果看func的3個形參地址逐漸升高,而3個局部變量地址逐漸降低。試說明為什么7/9/202314luanj@6.6(續)C語言中,實參從右向左進棧,所以func(i1,i2,i3)按i3,i2,i1的順序進棧而j1,j2,j3按聲明的順序分配7/9/202315luanj@6.7下面的C程序中,printf的調用僅含格式控制串,運行時輸出3個參數,分析之

main(){

printf(“%d%d%d\n”);

}7/9/202316luanj@6.7(續)C語言不做實參和形參個數類型是否一致的檢查printf函數根據第一個參數—格式控制列表,到棧中取參數本題中雖然只傳了格式控制列表,但是printf函數分析格式控制列表,認為程序員還傳了3個整型數,因此繼續去棧中取3個參數,并輸出之。所以得到了三個不可預知值得整數。7/9/202317luanj@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,%o\n”,&i,&j,&f,&e);

printf(“Addressofi1,j1,f1,e1=%o,%o,%o,%o\n”,&i1,&j1,&f1,&e1);

printf(“Addressofshort,int,long,float,double=%d,%d,%d,%d,%d\n”,sizeof(short),sizeof(int),sizeof(long),sizeof(float),sizeof(double));

}

main(){

shorti,j;floatf,e;

func(i,j,f,e);

}

運行結果:

Addressofi,j,f,e=35777772536,35777772542,35777772544,35777772554

Addressofi1,j1,f1,e1=35777772426,35777772426,35777772424,35777772420,35777772414

Sizeofshort,int,long,float,double=2,4,4,4,87/9/202318luanj@6.8(續)C語言為了不保證實參和形參類型一致,因此為了盡可能保證得到正確結果,編譯器在整型和實型做實參時,將他們提升為long和double傳遞。但是函數內部取參數時,仍按照原來的類型去取7/9/202319luanj@6.8(續)main傳參數時,提升數據類型func取參數時,按原來的數據類型i:short->int4字節j:short->int4字節f:long->double8字節e:long->double8字節i:short2字節j:short2字節f:long4字節e:long4字節7/9/202320luanj@6.9一個C程序

func(c,l)

charc;longl;

{

func(c,l);

}

在x86/Linux上編譯生成的匯編代碼如下,請說明char和long在參數傳遞和存儲分配上的區別7/9/202321luanj@6.9(續).file"parameter.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),%eax

pushl%eaxcallfuncaddl$8,%esp.L1:leaveret.Lfe1:.sizefunc,.Lfe1-func.ident"GCC:(GNU)egcs-2.91.6619990314/Linux(egcs-1.1.2release)”7/9/202322luanj@6.9(續)SomeAT&TASMSyntax寄存器:

8個32-bit寄存器%eax,%ebx,%ecx,%edx,%edi,%esi,%ebp,%esp;操作符源目的-1(%ebp):基址:%ebp,偏移:-1加在指令后的符號表示操作數的長度:

b(byte,8-bit)

w(word,16-bits)

l(long,32-bits)movsbl:

movs:符號擴展指令

movsbl意味著movs(from)byte(to)long;movbw意味著movs(from)byte(to)word;movswl意味著movs(from)word(to)long。More:plzgoogle“AT&TASM”7/9/202323luanj@6.9(續)movl8(%ebp),%eax --取cmovb%al,-1(%ebp) --取其字節值,存入分配的存儲單元movl12(%ebp),%eax--取lpushl%eax --l壓棧movsbl-1(%ebp),%eax--取c,并轉換成longpushl%eax --c壓棧callfunc --調用funcaddl$8,%esp --恢復壓棧前狀態7/9/202324luanj@6.10從例6.5可以看到,C程序執行時只用到了控制鏈,不需要使用訪問鏈.為什么Parscal程序執行時需要使用訪問鏈,而C程序不需要?7/9/202325luanj@6.10(續)PASCAL允許過程嵌套,執行時,可以訪問非全局且非局部的變量,所以需要訪問鏈幫助確定數據所在活動記錄在棧中的位置

而C不允許過程嵌套,只能訪問全局和局部變量,所以不需要訪問鏈。7/9/202326luanj@6.11下面是求階乘的Pascal程序.畫出程序第三次進入函數factor時的活動記錄棧和靜態鏈.

programfact(input,output);

varf,n:integer;

functionfactor(n:integer):integer;

begin

ifn=0thenfactor:=1

elsefactor:=n*(factor(n-1))

end;

beginn:=5;f:=factor(n);write(f)

end.7/9/202327luanj@6.11(續)factf,nfactor訪問鏈nfactor訪問鏈nfactor訪問鏈n1227/9/202328luanj@6.12在下面假想的程序中,第(11)行語句f:=a調用函數a,a傳遞函數addm作為返回值.

(a)畫出該程序執行的活動樹.

(b)假定非局部名字使用靜態作用域,為什么該程序在棧式分配情況下不能正確工作?

(c)在堆分配策略下,該程序的輸出是什么?7/9/202329luanj@6.12(續)programret(input,output);

varf:function(integer):integer; functiona:function(integer):integer

varm:integer;functionaddm(n:integer):integerbeginreturnm+nend;beginm:=0;returnaddmend;procedure

b(g:function(integer):integer);beginwriteln(g(2))end;beginf:=a;b(f)end7/9/202330luanj@6.12(續)活動樹如右圖在執行addm時,只有ret,b和addm的活動記錄,a的已釋放。如果是靜態作用域,n對應的是第(4)行中的m,他處于a的活動記錄中,而此時a的已釋放。堆分配結果是2retabaddm7/9/202331luanj@6.13為什么C語言允許函數類型(的指針)作為函數的返回值類型,而Pascal語言卻不允許?7/9/202332luanj@6.13(續)參考6.12課本P2027/9/202333luanj@6.14一個C語言程序如下:

intn;

intf(intg){

intm;

m=n;

if(m==0) return1;

else{

n=n-1;returnm*f(n);

}

}

main(){

n=5;printf(“%dfactorialis%d\n”,n,f(n));

}

該程序的運行結果不是我們所期望的

5factorialis120

而是

0factorialis120

試說明原因.7/9/202334luanj@6.14(續)參數逆序進棧,因此,f(n)先被執行,而f(n)執行結束時,n值已經被改寫為0,此時再將n值壓棧,因而輸出“0factorialis120”7/9/202335luanj@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;i<10;i++){

(*p)--;

j++;

}

}

addr(){

longk;

k=0;

p=&k;

}7/9/202336luanj@6.15(續)程序運行時陷入死循環的原因是由于p指向分配給i的存儲單元引起的。循環體執行一次便停止時由于p指向分配給i的高位字節引起的。C語言的實現是采用棧式分配,使得函數addr和函數loop的活動記錄先后從同樣的存儲單元開始分配,從而長整數k和i先后分配在同樣的存儲單元,因此p指向分配給i的存儲單元。SPARC/SUN工作站上整數的存放方式是低地址放高位字節,而高地址放低位字節。另外,活動記錄棧是從高地址向低地址方向增長。當k改為短整型且p改為短整型指針后,那么p指向由i的兩個低位字節組成的短整數。執行(*p)--使得這兩個字節構成的短整數等于-1。而從整個4個字節看時,是65535,遠大于10。所以循環體執行一次便停止。7/9/202337luanj@6.16一個C語言程序

main()

{

func();

printf(“Returnfromfunc\n”);

}

func()

{

chars[4];

strcpy(s,”12345678”);

printf(“%s\n”,s);

}

在X86/Linux操作系統上的運行結果如下:

12345678

Returnfromfunc

Segmentationfault(coredumped)

試分析為什么會出現這樣的運行錯誤.7/9/202338luanj@6.16(續)數組越界訪問。出現短錯誤,說明控制鏈被破壞func可以返回main說明func的返回地址沒有被破壞,而ma

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論