《編譯原理》課件第6章_第1頁
《編譯原理》課件第6章_第2頁
《編譯原理》課件第6章_第3頁
《編譯原理》課件第6章_第4頁
《編譯原理》課件第6章_第5頁
已閱讀5頁,還剩81頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

第6章運行時存儲空間組織6.1靜態(tài)存儲分配6.2簡單的棧式存儲分配6.3嵌套過程語言的棧式實現(xiàn)6.4堆式動態(tài)存儲分配*6.5參數(shù)傳遞補遺習題六6.1靜態(tài)存儲分配如果在編譯時就能夠確定一個程序在運行時所需的存儲空間大小,則在編譯時就能夠安排好目標程序運行時的全部數(shù)據(jù)空間,并能確定每個數(shù)據(jù)項的單元地址,存儲空間的這種分配方法叫做靜態(tài)分配。對FORTRAN語言來說,其特點是不允許過程有遞歸性,每個數(shù)據(jù)名所需的存儲空間大小都是常量(即不允許含可變體積的數(shù)據(jù),如可變數(shù)組),并且所有數(shù)據(jù)名的性質是完全確定的(不允許出現(xiàn)在運行時再動態(tài)確定其性質的名字這種情況)。這些特點確保整個程序所需數(shù)據(jù)空間的總量在編譯時是完全確定的,從而每個數(shù)據(jù)名的地址就可靜態(tài)地進行分配。靜態(tài)存儲分配是一種最簡單的存儲管理。一般而言,適于靜態(tài)存儲分配的語言必須滿足以下條件:

(1)數(shù)組的上下界必須是常數(shù);

(2)過程調(diào)用不允許遞歸;

(3)不允許采用動態(tài)的數(shù)據(jù)結構(即在程序運行過程中申請和釋放的數(shù)據(jù)結構)。滿足這些條件的語言除了FORTRAN之外,還有BASIC等語言。在這些語言中,編譯程序可以完全確定程序中數(shù)據(jù)項所在的地址(通常為相對于各數(shù)據(jù)區(qū)起始地址的位移量)。由于過程調(diào)用不允許遞歸,因此數(shù)據(jù)項的存儲地址就與過程相聯(lián)系。過程調(diào)用所使用的局部數(shù)據(jù)區(qū)可以直接安排在過程的目標代碼之后,并把各數(shù)據(jù)項的存儲地址填入相關的目標代碼中,以便在過程運行時訪問這個局部數(shù)據(jù)區(qū)。在此,不存在對存儲區(qū)的再利用問題,目標程序執(zhí)行時不必進行運行時的存儲空間管理,過程的進入和退出變得極為簡單。

FORTRAN語言的靜態(tài)存儲管理特點是FORTRAN程序中的各程序段均可獨立地進行編譯。在編譯過程中,給程序中的變量或數(shù)組分配存儲單元的一般做法是:為每一個變量(或數(shù)組)確定一個有序的整數(shù)對;其中,第一個整數(shù)用來指示數(shù)據(jù)區(qū)(局部數(shù)據(jù)區(qū)或公用區(qū))的編號,第二個整數(shù)則用來指明該變量(或數(shù)組)所對應的存儲起始單元相對于其所在數(shù)據(jù)區(qū)起點的位移(即相對于數(shù)據(jù)區(qū)起點的地址),并將這一對整數(shù)填入符號表相應登記項的信息欄中。至于各數(shù)據(jù)區(qū)的起始地址在編譯時可暫不確定,待各程序段全部編譯完成之后,再由連接裝配程序最終確定,并將各程序段的目標代碼組裝成一個完整的目標程序。一個FORTRAN程序段的局部數(shù)據(jù)區(qū)可由圖6–1所示的項目組成。其中,隱參數(shù)是指過程調(diào)用時的連接信息(不在源程序中明顯出現(xiàn)),如調(diào)用時的返回地址、調(diào)用時寄存器的保護等;形式單元用來存放過程調(diào)用時形參與實參結合的實參地址或值。

圖6–1一個FORTRAN程序段的局部數(shù)據(jù)區(qū)6.2簡單的棧式存儲分配我們首先考慮一種簡單程序語言的實現(xiàn),這種語言沒有分程序結構,過程定義不允許嵌套,但允許過程的遞歸調(diào)用,允許過程含有可變數(shù)組。例如,C語言除不允許含有可變數(shù)組外,就是這樣一種語言。C語言的程序結構如下:全局數(shù)據(jù)說明voidmain(?){

main中的數(shù)據(jù)說明}voidR(?){

R中的數(shù)據(jù)說明}voidQ(){

Q中的數(shù)據(jù)說明}例如,下面計算n!的C語言程序就是一個遞歸調(diào)用的程序,它的執(zhí)行過程可以用棧來實現(xiàn):#include“stdio.h”longfactorial(intn){

if(n>1)

return(n*factorial(n?1));

else

return(1);}voidmain(?){intnum;do{scanf(“%d”,&num);if(num>=0&&num<15)

printf(“%d\n”,factorial(num));else

printf(“error!\n”);}while(num>=0);}6.2.1棧式存儲分配與活動記錄使用棧式存儲分配法意味著程序運行時,每當進入一個過程(或函數(shù))就有一個相應的活動記錄累筑于棧頂,此記錄含有連接數(shù)據(jù)、形式單元、局部變量、局部數(shù)組的內(nèi)情向量和臨時工作單元等;在進入過程和執(zhí)行過程的可執(zhí)行語句之前,再把局部數(shù)組所需空間累筑于棧頂,從而形成過程工作時的完整數(shù)據(jù)區(qū)。注意,每個過程的活動記錄的體積在編譯時可以靜態(tài)地確定。但由于允許含有可變數(shù)組,所以數(shù)組的大小只有在運行時才能知道。因數(shù)組區(qū)的大小不能預先獲知,為了擴充方便,所以只能將數(shù)組區(qū)累筑于活動記錄之上的當前棧頂。當一個過程工作完畢返回時,它在棧頂?shù)臄?shù)據(jù)區(qū)(包括活動記錄和數(shù)組區(qū))也隨即不復存在。對C語言來說,由于其不含可變數(shù)組,因而它的活動記錄本身包含了局部數(shù)組的空間。圖6–2和圖6–3分別給出了C語言和含可變數(shù)組的某簡單語言程序運行時的數(shù)據(jù)空間結構,即顯示了主程序在調(diào)用了過程Q,Q又調(diào)用了過程R,且在R投入運行后的存儲結構。SP指示器總是指向執(zhí)行過程活動記錄的起點,而TOP指示器則始終指向(已占用)棧頂單元。當進入一個過程時,TOP指向為此過程創(chuàng)建的活動記錄的頂端;在分配數(shù)組區(qū)之后(如果有的話),TOP又改為指向數(shù)組區(qū)(從而是該過程整個數(shù)據(jù)區(qū))的頂端。圖6–2C語言程序的存儲組織

圖6–3含可變數(shù)組程序的存儲組織

C的活動記錄含有以下幾個區(qū)段(如圖6–4所示):

(1)連接數(shù)據(jù)(兩項):老SP值(即前一活動記錄的起始地址)和返回地址;

(2)參數(shù)個數(shù);

(3)形式單元(存放實在參數(shù)的值或地址);

(4)過程的局部變量(簡單變量)、數(shù)組的內(nèi)情向量和臨時工作單元。

C語言不允許過程(函數(shù))嵌套,也即不允許一個過程的定義出現(xiàn)在另一個過程的定義之內(nèi)。因此,C語言的非局部變量僅能出現(xiàn)在源程序頭,非局部變量可采用靜態(tài)存儲分配并在編譯時確定它們的地址。

圖6–4C過程的活動記錄由圖6–4可知,過程的每一局部變量或形參在活動記錄中的位置都是確定的;也就是說,這些變量或形參所分配的存儲單元其地址都是相對于活動記錄的基址SP的。因此,變量和形參運行時在棧上的絕對地址是:絕對地址=活動記錄基址(SP)+相對地址于是,對當前正在執(zhí)行(即活動)的過程,其任何局部變量或形參X的引用均可表示為變址訪問X[SP]。此處X代表X相對于活動記錄基址的偏移量,這個偏移量(即相對數(shù))在編譯時可完全確定下來。過程的局部數(shù)組的內(nèi)情向量的相對地址在編譯時也同樣可完全確定下來,一旦數(shù)據(jù)空間在過程里獲得分配,對數(shù)組元素的引用也就容易用變址的方式進行訪問。6.2.2過程的執(zhí)行

1.過程調(diào)用過程調(diào)用的四元式序列為

parT1 parT2

parTn callP,n由于此時TOP指向被調(diào)用過程P之前的棧頂,而P的形式單元和活動記錄起點之間的距離是確定的(等于3,參見圖6–4),因而由調(diào)用者過程給將要調(diào)用的過程P的活動記錄(正在形成中)的形式單元傳遞實參值或實參地址,即每個parTi(i=1,2,…,n)可直接翻譯成如下指令:

(i+3)[TOP]=Ti

//傳遞參數(shù)值或 (i+3)[TOP]=addr[Ti] //傳遞參數(shù)地址而四元式callP,n則翻譯成:

1[TOP]=SP //保護現(xiàn)行SP 3[TOP]=n //傳遞參數(shù)個數(shù)

JSRP//轉子指令,轉向P過程的第一條指令過程P調(diào)用之前,先構造出P的活動記錄部分內(nèi)容,如圖6–5所示。圖6–5過程P調(diào)用前先構造P的活動記錄部分內(nèi)容

2.過程進入轉入過程P后,首先要做的工作是定義新活動記錄的SP,保護返回地址和定義新活動記錄的TOP值,即執(zhí)行下述指令:

SP=TOP+1 //定義新SP1[SP]=返回地址 //保護返回地址

TOP=TOP+L //定義新TOP其中,L是過程P的活動記錄所需的單元數(shù),這個數(shù)在編譯時可靜態(tài)地計算出來。對含可變數(shù)組(非C語言)的情況來說,因為過程可含可變數(shù)組且所有數(shù)組都分配在活動記錄的頂上,所以緊接上述指令之后應是對數(shù)組進行存儲分配的指令(如果含有局部數(shù)組),這些指令是在翻譯數(shù)組說明時產(chǎn)生的。對每個數(shù)組說明,相應的目標指令組將做以下幾件工作:

(1)計算各維的上、下限;

(2)調(diào)用數(shù)組空間分配子程序,其參數(shù)是各維的上、下限和內(nèi)情向量單元首地址。數(shù)組空間分配子程序計算并填好內(nèi)情向量的所有信息,然后在TOP所指的位置之上留出數(shù)組所需的空間,并將TOP調(diào)整為指向數(shù)組區(qū)的頂端。?進入過程P后所做的工作如圖6–6所示。圖6–6進入過程P后所做的工作示意

3.過程返回

C語言以及其它一些相似的語言含有return(E)形式的返回語句,其中E為表達式。假定E值已計算出來并存放在某臨時單元T中,則此時即可將T值傳送到某個特定的寄存器中(調(diào)用過程將從這個特定的寄存器中獲得被調(diào)用過程P的結果)。剩下的工作就是恢復SP和TOP為進入過程P之前的原值(即指向調(diào)用過程的活動記錄及工作空間),并按返回地址實行無條件轉移,即執(zhí)行下述指令序列:

TOP=SP–1 //恢復調(diào)用過程的TOP值

SP=0[SP] //恢復調(diào)用過程的SP值

X=2[TOP] //將返回地址送XUJ0[X]//無條件轉移,即按X的地址返回到調(diào)用過程一個過程也可通過它的end語句(對C語言則是該過程(函數(shù))體結束時的“}”)自動返回。如果此過程是一個函數(shù)過程,則按上述辦法傳遞結果值,否則僅直接執(zhí)行上述返回指令序列。過程P的返回示意如圖6–7所示。圖6–7過程P的返回示意

6.3嵌套過程語言的棧式實現(xiàn)6.3.1嵌套層次顯示(DISPLAY)表和活動記錄在討論中,常常要用到過程定義的“嵌套層次”(簡稱層數(shù))這個概念。我們始終假定主程序的層數(shù)為0,因此主程序稱為第0層過程。如果過程Q是在層數(shù)為i的過程P內(nèi)定義的,并且P是包圍Q的最小過程,則Q的層數(shù)就為i+1。當編譯程序處理過程說明時,過程的層數(shù)將作為過程名的一個重要屬性登記在符號表中。一種常用的跟蹤每個外層過程最新活動記錄位置的有效辦法是,每進入一個過程后,在建立它的活動記錄區(qū)的同時建立一張嵌套層次DISPLAY表。假定現(xiàn)在進入的過程層數(shù)為i,則它的DISPLAY表含有i+1個單元。此表本身是一個小棧,自頂而下每個單元依次存放著現(xiàn)行層、直接外層……直至最外層(第0層,即主程序層)的每一層的最新活動記錄的起始地址。例如,令過程R的外層為Q,Q的外層為主程序P,則過程R運行時的DISPLAY表內(nèi)容如表6.1所示。由于過程定義是嵌套的,因而一個過程可以引用包圍它的任一外層過程所定義的變量或數(shù)組。也就是說,運行時,一個過程Q可能引起它的任一外層過程P的最新活動記錄中的某些數(shù)據(jù)。因此,過程Q運行時必須知道它的所有外層的最新活動記錄的起始地址。由于允許遞歸和可變數(shù)組的存在,過程的活動記錄位置(即使是相對位置)也往往是變遷的,因而必須設法跟蹤每個外層過程的最新活動記錄的位置(起始地址)。例如,下面的PASCAL程序其活動記錄在棧中的示意如圖6-8所示(調(diào)用順序為:env→a→b→c→b→c)。programenv; procedurea; varx:integer; procedureb; procedurec; beginx:=x-1;ifx>0thenbend;{procedurec} begincend;{procedureb} beginbend;{procedurea} beginx:=2;aend.{main}圖6-8活動記錄在棧中的示意由圖6-8可知,過程c訪問過程a定義的變量x時,都要根據(jù)每層活動記錄所保存的老SP值逐層返回(見圖6-8箭頭所示)方可找到。這種做法過于麻煩,能否采取一種更為簡單有效的方法呢?一種常用的跟蹤每個外層過程最新活動記錄位置的有效辦法是,每進入一個過程后,在建立它的活動記錄區(qū)的同時建立一張嵌套層次DISPLAY表,將記錄它所有外層過程最新活動記錄起始位置的SP值都放在這張嵌套層次DISPLAY表中;這樣,就可直接在本層查到它的任何一個外層過程最新活動記錄起始位置。假定現(xiàn)在進入的過程層數(shù)為i,則它的DISPLAY表含有i+1個單元。此表本身是一個小棧,自頂而下每個單元依次存放著現(xiàn)行層、直接外層、……直至最外層(第0層,即主程序層)的每一層的最新活動記錄的起始地址。例如,令過程R的外層為Q,Q的外層為主程序P,則過程R運行時的DISPLAY表內(nèi)容如表6.1所示。由于過程的層數(shù)可靜態(tài)確定,因此每個過程的DISPLAY表的體積在編譯時即可知道。為了便于組織存儲區(qū)和簡化處理手續(xù),我們把DISPLAY表作為活動記錄的一部分置于形式單元的上端,如圖6-8所示。由于每個過程的形式單元數(shù)目在編譯時是知道的,因此DISPLAY表的相對地址d(相對于活動記錄的起點)在編譯時也是完全確定的。被調(diào)用過程為了建立自己的DISPLAY表,就必須知道它的直接外層過程的DISPLAY表,這意味著必須把直接外層的DISPLAY表地址作為連接數(shù)據(jù)之一(稱為“全局DISPLAY表地址”)傳送給被調(diào)用過程。于是,此時的連接數(shù)據(jù)包含老SP值、返回地址和全局DISPLAY表地址這三項內(nèi)容。整個活動記錄的結構如圖6-9所示。圖6–9活動記錄結構

6.3.2嵌套過程的執(zhí)行

1.過程調(diào)用過程調(diào)用所做的工作與簡單棧式存儲分配大體相同,只是增加了一個連接數(shù)據(jù),所以每個parTi

相應的指令應改為

(i+4)[TOP]=Ti

或者 (i+4)[TOP]=addr[Ti]

callP,n所對應的指令應為

1[TOP]=SP //保護現(xiàn)行SP 3[TOP]=SP+d //將直接外層的DISPLAY表起始地址作為P的全局DISPLAY表地址

4[TOP]=n //傳遞參數(shù)個數(shù)

JSRP //轉向P的第一條指令

2.過程進入轉入過程P后,首先執(zhí)行和簡單棧式存儲分配相同的指令:

SP=TOP+1 //定義新的SP1[SP]=返回地址 //保護返回地址

TOP=TOP+L //定義新的TOP其次,應按第三項連接數(shù)據(jù)所提供的全局DISPLAY表地址,自下而上地抄錄k個單元內(nèi)容(k為P的層次),最后再添上新的SP值形成現(xiàn)行過程P的DISPLAY表(共k+1個單元)。其過程如圖6–10所示。圖6–10過程P進入示意

3.過程返回當過程P工作完畢要返回到調(diào)用段時,若return語句含有返回值或P是函數(shù)過程,則把已算好的值傳送到某個特定的寄存器,然后執(zhí)行:

TOP=SP?1 //恢復調(diào)用過程的TOP值

SP=0[SP] //恢復調(diào)用過程的SP值

X=2[TOP] //將返回地址送X UJ0[X] //無條件轉移,返回過程返回執(zhí)行的指令與簡單棧式存儲分配的過程返回完全一樣。6.3.3訪問非局部名的另一種實現(xiàn)方法在允許嵌套的過程中,一個過程可以引用包圍它的任一外層過程所定義的變量或數(shù)組;也即在運行時,一個過程Q可能引用它的任一外層過程P的最新活動記錄中的某些數(shù)據(jù)(這些數(shù)據(jù)視為過程Q的非局部量)。為了在活動記錄中查找非局部名字所對應的存儲空間,過程Q運行時必須知道它的所有外層過程的最新活動記錄的地址。因為過程活動記錄的位置(即使是相對位置)往往也因過程的遞歸而變遷,所以必須設法跟蹤每個外層過程的最新活動記錄的位置。跟蹤的一種有效辦法是采用嵌套層次顯示(DISPLAY)表,其優(yōu)點是訪問非局部量的速度較快。在此,我們介紹另一種訪問非局部名的方法——存取鏈(也稱靜態(tài)鏈)方法。存取鏈方法引入一個稱為存取鏈的指針,該指針作為活動記錄的一項指向直接外層的最新活動記錄的地址,這就意味著在運行時棧中的每個數(shù)據(jù)區(qū)(活動記錄)之間又拉出一條鏈,這個鏈稱為存取鏈。注意,運行時棧中數(shù)據(jù)區(qū)之間原先就存在一條鏈,即每個活動記錄中所保存的老SP值這一項,它是指向調(diào)用該過程(子過程)的那個過程(父過程)的最新活動記錄的起點,由此向前形成了一條SP鏈。為了區(qū)別于存取鏈,稱SP鏈為控制鏈(也稱動態(tài)鏈),它記錄了在運行中過程之間相互調(diào)用的關系。注意,控制鏈是動態(tài)的,而存取鏈是靜態(tài)的??刂奇溣涗浟水斍皶r刻程序中各過程相互調(diào)用的情況;而存取鏈則始終記錄著程序靜態(tài)定義時該過程所有的直接外層(嵌套過程規(guī)定,內(nèi)層過程只允許調(diào)用其靜態(tài)定義時的外層過程說明的變量和數(shù)組)。因此,存取鏈指出了一個過程的當前活動記錄指向其直接定義的外層過程直至最外層的最新活動記錄的起點。具有存取鏈的活動記錄結構如圖6–11所示。圖6–11具有存取鏈的活動記錄結構假定過程的嵌套關系如下:程序中每個過程的靜態(tài)結構(嵌套層次)是確定的,如嵌套深度為2的過程R引用了非局部量a和b,其嵌套深度分別為0和1。從R的活動記錄開始,分別沿著2???0?=?2和2?1=1個存取鏈向前查找,則可找到包含這兩個非局部量的活動記錄。上述過程P調(diào)用S以及S調(diào)用Q運行時棧的變化過程如圖6–12(a)、(b)所示。由圖6–12可以看出,指針SP總是指向當前正在執(zhí)行過程的活動記錄起點,控制鏈(老SP)則指向調(diào)用運行過程的父過程的活動記錄起點。因此,當運行過程調(diào)用結束返回時,利用控制鏈老SP值可以得到調(diào)用前原父過程活動記錄的起點。從程序的靜態(tài)結構來看,P是S和Q的靜態(tài)直接外層,因此,S和Q活動記錄中的存取鏈均指向其直接外層P的活動記錄起點。圖6–12過程調(diào)用時運行棧的變化(a)P調(diào)用S;(b)S調(diào)用Q例6.1

某程序的結構如圖6–13所示,其中A、B、C為過程名,請分別畫出過程C調(diào)用A前后的棧頂活動記錄。圖6–13例6.1的程序結構示意

[解答]過程C調(diào)用A前后的棧頂活動記錄示意如圖6–14所示。由圖6–13可知,當過程C執(zhí)行時,它可使用主程序、A、B和C過程所說明的變量,且其外層嵌套的過程活動記錄起始地址由DISPLAY表指出。當C調(diào)用A而使過程A執(zhí)行時,我們看到此時的DISPLAY表已變?yōu)閮身?,即主程序和A過程自身;也即此時A只可使用主程序和A過程所說明的變量。圖6–14例6.1的運行棧與活動記錄示意

例6.2

在下面的PASCAL程序中,已經(jīng)第二次(遞歸地)進入了f,請給出第三次進入f后的運行棧及DISPLAY表的示意圖。

PROGRAMtest(input,output);VARK:integer;FUNCTIONf(n:integer):integer;BEGINIFn<=0THENf:=1ELSEf:=n*f(n?1)END;

BEGIN

K:=f(10);

write(K)

END.

[解答]第三次進入f后的運行棧及DISPLAY表的示意圖如圖6–14所示。由于靜態(tài)嵌套層次只有兩層,故每一次遞歸調(diào)用產(chǎn)生的DISPLAY表只有兩項,一項是test的SP(即0),另一項是當前活動記錄的SPi(i=1,2,3)。圖6–15例6.2的運行棧及DISPLAY表示意圖6.4堆式動態(tài)存儲分配6.4.1堆式存儲的概念如果一種程序語言允許數(shù)據(jù)對象能夠自由地分配和釋放,或者不僅有過程而且有進程(Process)這樣的程序結構,那么由于空間的使用不一定遵循“先申請后釋放”的原則,則棧式存儲分配就不適用了。在這種情況下,通常使用一種稱之為堆的動態(tài)存儲分配方案。假定程序運行時有一個大的存儲空間,需要時就從這個空間中借用一塊,不用時再退還給它。由于借、還的時間先后不一,因而經(jīng)過一段時間的運行后,這個大空間就必然被分割成如圖6–16所示的許多小塊,這些塊有些正在使用,有些則是空閑的(未被使用)。圖6–16堆式存儲分配示意

對于堆式存儲分配來說,需要解決兩個問題:一是堆空間的分配,即當運行程序需要一塊空間時應分配哪一塊給它;另一個問題是分配空間的回收,由于返回堆的不用空間是按任意次序進行的,所以需要研究專門的回收分配策略。在許多語言中都有顯式的堆空間分配和回收語句或函數(shù),如PASCAL語言中的new和dispose、C語言中的alloc和free以及C++語言中的new和delete。6.4.2堆式存儲管理的方法由于堆式分配方式和存儲管理技術較為復雜,并且有效的堆管理是數(shù)據(jù)結構課程研究的問題,故我們只對堆式分配方式作簡單的討論。當運行程序要求一塊體積為N的存儲空間時應如何分配?從理論上講,這時應從比N稍大一些的空閑塊中取出N個單元予以分配,這種做法的目的是保持較大的空閑塊以備將來之需。但這種方法實現(xiàn)起來難度較大,實際中采用的辦法是:掃描空閑塊鏈并在首次遇到的比N大的空閑塊中取出N個單元進行分配。如果找不到一塊比N大的空閑塊,但所有空閑塊的總和卻比N大,這時就需要用某種方法使這些空閑塊拼接在一起,形成一個可分配的連續(xù)空間。如果所有空閑塊的總和都不及N大,則需要采用更復雜的辦法,如廢品回收技術(即尋找那些運行程序已不使用但仍未釋放的存儲塊或運行程序目前很少使用的存儲塊),把這些存儲塊回收后再重新分配??梢圆捎枚喾N策略進行堆式動態(tài)存儲管理。在此,我們介紹一種使用可利用空間表進行動態(tài)分配的方法??衫每臻g表是指將所有空閑塊用一張表記錄下來,表的結構可以是目錄表,也可以是鏈表,其結構分別如圖6–17(b)、(c)所示。圖6–17內(nèi)存狀態(tài)和可利用空間表(a)內(nèi)存狀態(tài);(b)目錄表;(c)鏈表使用可利用空間表進行動態(tài)存儲分配的方法又可分為如下兩種:

(1)定長塊的管理。最簡單的堆式存儲管理方法是采用定長塊的管理方法,即將堆存儲空間在初始化時就劃分成大小相同的若干塊,將各個塊通過鏈表鏈接起來形成一個單向線性鏈表。由于各塊大小相同,故分配時無需查找,只需將頭指針所指的第一塊分配給用戶即可,然后頭指針指向下一塊。同樣,當回收時,系統(tǒng)將待回收的存儲塊插入到表頭即完成了該塊的回收。

(2)變長塊的管理。變長塊管理方法是一種常用的堆式存儲管理方法,它可以根據(jù)實際需要來分配長度不同的空閑塊;對空閑塊的管理則可以采用圖6–17(c)中的鏈表形式。系統(tǒng)開始時,存儲空間是一完整空間,可利用空間表中只有一個大小為整個存儲空間的空閑塊。在系統(tǒng)運行一段時間后,隨著分配和回收的進行,可利用空間表中空閑塊的大小和個數(shù)也隨之改變。由于可利用空間表中的空閑塊大小不同,因而存在著如何進行空閑塊分配的問題。若可利用空間表存在多個大于所要求空間的空閑塊,可采取以下三種方法之一進行存儲分配:

(1)首次滿足法。從表頭開始查找可利用空間表,將找到的第一個滿足需要的空閑塊或空閑塊的一部分分配出去(當空閑塊略大于所要求的空間時,則整塊分配出去),而其余部分仍作為一個空閑塊留在表中。

(2)最優(yōu)滿足法。系統(tǒng)掃描整個可利用空間表,從中找出一塊不小于要求的最小空閑塊予以分配。為了避免每次分配都要掃描整個表,通常將空閑塊按由小到大的順序進行排列。這樣,所找到的第一個大于或等于所需空間的空閑塊即為所求,無須再掃描整個表。

(3)最差滿足法。系統(tǒng)將可利用空間表中最大的空閑塊予以分配(當然也要求其不小于所需空間的大小),這種方法應使空閑塊按由大到小的順序排列,此時表頭的空閑塊即為所求。最優(yōu)滿足法和最差滿足法在回收時都需將待回收的空閑塊插入到鏈表中適當?shù)奈恢蒙先?。以上三種方法各有所長。一般來說,最優(yōu)滿足法適用于請求分配的內(nèi)存大小范圍較廣的系統(tǒng);最差滿足法適用于請求分配的內(nèi)存大小范圍較窄的系統(tǒng);而首次滿足法則適用于事先無法獲知請求分配和回收情況的系統(tǒng)。從時間上來看,最優(yōu)滿足法無論分配與回收都需要查表,故最費時間;最差滿足法分配時無需查表,但回收時卻需查表并根據(jù)回收空閑塊的大小確定其在表中應插入的位置;而首次滿足法在分配時需要查表,回收時直接插入到表頭即可。對于已分配的存儲塊,可以采用不同的回收策略。有的程序語言干脆不做回收工作,直到內(nèi)存空間用完為止;如果當空間用完時還有分配存儲塊的請求,就停止程序的運行。這樣做的缺點是浪費空間,但如果系統(tǒng)具有海量虛存或堆中的多數(shù)數(shù)據(jù)是一分配就一直使用的情形,則這種方法也是可行的。如果程序語言有顯式的分配命令,那么就可用顯式的回收命令(如C語言中的free)來回收不用的空間。*6.5參數(shù)傳遞補遺定義和調(diào)用過程是程序語言的主要特征之一。過程是模塊程序設計的主要手段,同時也是節(jié)省程序代碼和擴充語言能力的主要途徑。PASCAL語言的設計者N.Wirth曾經(jīng)說過:“在程序設計技巧中,過程是很少幾種基本工具中的一種,掌握了這種工具,就能對程序員工作的質量和風格產(chǎn)生決定性的影響?!币粋€過程一旦定義后,就可以在別的地方調(diào)用它。調(diào)用與被調(diào)用(過程)兩者之間的信息往來通過全局量或參數(shù)傳遞。例如,下面的C語言程序:#include“stdio.h”voidshowme(inta,intb,intc){

printf(“a=%d,b=%d,c=%d\n”,a,b,c);}voidmain(?){

intx=10,y=20,z=30;

showme(z,y,x);}就是一個含函數(shù)調(diào)用的程序。其中a、b、c稱為形式參數(shù)(簡稱形參),而函數(shù)調(diào)用語句:

showme(z,y,x)中的z、y、x則稱為實在參數(shù)(簡稱實參)。實參甚至也可以是一個較復雜的表達式而不僅僅只是一個變量。實參和對應的形參在性質上應相容不悖。6.5.1參數(shù)傳遞的方法

1.傳值傳值是最簡單的參數(shù)傳遞方法。所謂傳值,就是計算出實在參數(shù)的值然后把它傳給被調(diào)用過程相對應的形式參數(shù),具體過程如下:

(1)把形式參數(shù)當作過程的局部變量處理,即在被調(diào)用過程的活動記錄中開辟形式參數(shù)的存儲空間(即形式單元)。

(2)調(diào)用過程計算出實在參數(shù)的值,并將該值放入為形式單元開辟的空間中。

(3)被調(diào)用過程執(zhí)行時就像使用局部變量一樣使用這些形式單元。傳值的一個重要特點是對形式參數(shù)的任何運算都不影響調(diào)用過程的活動記錄中實在參數(shù)的值,即參數(shù)傳遞后實在參數(shù)與對應的形式參數(shù)不再發(fā)生聯(lián)系了。

2.傳地址所謂傳地址,是指把實在參數(shù)的地址傳遞給相應的形式參數(shù)所對應的形式單元。如果實在參數(shù)是一個變量(包括下標變量),則直接將該變量的地址傳給相應的形式單元;如果實在參數(shù)是常數(shù)或表達式,則先計算其值并存放在某一臨時單元中,然后將這個臨時單元的地址傳給相應的形式單元。被調(diào)用過程執(zhí)行時,對形式參數(shù)的任何引用或賦值都被處理成對形式單元的間接訪問,即按形式單元中存放的地址轉到調(diào)用過程的活動記錄中去訪問實在參數(shù)。對形式參數(shù)的任何運算實際上都是對實在參數(shù)的運算,而形式參數(shù)只不過起到輔助查找到實在參數(shù)的指針的作用。因此,當被調(diào)用過程工作完畢返回時,形式單元所指的實在參數(shù)單元就保留了運算的結果。

3.傳名傳名是高級語言ALGOL60所定義的一種特殊的參數(shù)傳遞方式,其傳遞參數(shù)的方法如下:

(1)過程調(diào)用的作用相當于把被調(diào)用過程的過程體復制到調(diào)用處(替換調(diào)用語句),并將過程體中所有出現(xiàn)的形式參數(shù)在文字上替換成相應的實在參數(shù)。這種文字上的替換稱為宏擴展(MarcroExpansion)。

(2)被調(diào)用過程中的局部名如果與過程調(diào)用的實在參數(shù)名發(fā)生沖突,則在宏擴展前對被調(diào)用過程中的這些局部名重新命名以避免重名沖突。

(3)為表現(xiàn)實在參數(shù)的整體性,必要時在替換前把實在參數(shù)用括號括起來。傳名這種參數(shù)傳遞方法因其操作過于復雜現(xiàn)在已很少采用。6.5.2不同參數(shù)傳遞方法的比較為了描述不同參數(shù)傳遞方法下程序的執(zhí)行,我們將動態(tài)棧和活動記錄結合起來簡化為一種動態(tài)圖。采用動態(tài)圖的方法來對程序的執(zhí)行進行描述時,記錄主程序和過程(或函數(shù))的調(diào)用、運行及撤消各個階段的狀態(tài),以及程序運行期間所有變量和過程(或函數(shù))中傳值與傳地址的變化過程。動態(tài)圖規(guī)則如下:

(1)動態(tài)圖縱向描述主程序、過程或函數(shù)各層之間的調(diào)用關系,橫向由左至右按執(zhí)行的時間順序記錄主程序、過程(或函數(shù))中各變量值的變化情況。

(2)過程(或函數(shù))的傳值的形式參數(shù)均看作是帶初值的局部變量(也可用箭頭來表示實在參數(shù)傳給形式參數(shù)的指向),其后,形式參數(shù)就作為局部變量參與過程(或函數(shù))的操作。對于傳地址方式,由于形式參數(shù)的作用就像指向實在參數(shù)的指針,故動態(tài)圖中形式參數(shù)一律指向與其對應的實在參數(shù)變量(注意,兩者位于動態(tài)圖相鄰的兩層上;如果實在參數(shù)為表達式,則用一個臨時變量來代表這個表達式);此后,所有對形式參數(shù)的操作都是根據(jù)形式參數(shù)箭頭所指對實參變量進行的。

(3)主程序,過程(或函數(shù))按運行中的調(diào)用關系由上向下分層,各層(相當于活動記錄)說明的變量(包括形式參數(shù))都依次列于該層首列,各變量值的變化情況按時間順序記錄在與該變量對應的同一行上。以下面的程序為例,對三種參數(shù)傳遞方法進行比較:programparament;int?A,B;procedureP(x,y,z);{ Y=Y+1; Z=Z+X}{A=2;B=3;P(A+B,A,A);printA}

(1)傳值:用T代表A+B的臨時變量,則對圖6–18所示的動態(tài)圖分析得A=2。

(2)傳地址:用T代表A+B的臨時變量,則對圖6–19所示的動態(tài)圖分析得A=8。圖6–18傳值時的動態(tài)圖

圖6–19傳地址時的動態(tài)圖

(3)傳名:由于傳名時的過程調(diào)用就是把過程體抄到調(diào)用出現(xiàn)的地方,所以實際執(zhí)行的程序為

A=2;

B=3;

A=A+1; //形參Y換成A

A=A+(A+B); //形參Z換成A,形參X換成(A+B)

printA經(jīng)分析得A=9。不同的參數(shù)傳遞方法得到的結果不同,因此,如何選擇參數(shù)傳遞的方法將影響語言的語義,這是編譯程序在處理參數(shù)傳遞時應引起重視的問題。習題六

6.1完成下列選擇題:

(1)分配目標程序數(shù)據(jù)空間的基本策略分為

A.棧式分配和堆式分配B.局部分配和整體分配

C.靜態(tài)分配和動態(tài)分配D.程序運行之前分配

(2)過程的DISPLAY表中記錄了

。

A.過程的連接數(shù)據(jù)B.過程的嵌套層次

C.過程的返回地址D.過程的入口地址

(3)過程P1調(diào)用P2時,連接數(shù)據(jù)不包含

。

A.嵌套層次顯示表 B.老SP值

C.返回地址 D.全局DISPLAY表地址

(4)堆式動態(tài)分配申請和釋放存儲空間遵守

原則。

A.先請先放B.先請后放C.后請先放D.任意(5)棧式動態(tài)分配與管理在過程返回時應做的工作有

。

A.保護老SP

溫馨提示

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

評論

0/150

提交評論