运行时存储空间组织
??對于存儲空間這章不是課程的重點(diǎn),老師講授的內(nèi)容比較少。不過還是做一點(diǎn)兒整理,供以后復(fù)習(xí)參考使用。
??學(xué)習(xí)本章的重點(diǎn)在于了解不同情況下的內(nèi)存分配策略,大致分為三種:靜態(tài)分配、動態(tài)棧式分配、動態(tài)堆式分配。對于棧式只介紹一些很簡單的,對于堆式?jīng)]有介紹。其實(shí)這樣也沒有什么考點(diǎn),權(quán)當(dāng)擴(kuò)充知識,了解在編譯過程中內(nèi)存的變化情況。
??學(xué)習(xí)存儲分配,有幾個基本的概念是要了解的(不一定要背下來,至少說出來要知道是啥吧~):
??這里面提到一個概念叫生存期,之前學(xué)高級語言的時候都講變量都有作用域。那這兩個有什么區(qū)別呢?生存期是有效時間長短,作用域是有效作用范圍。有點(diǎn)兒像時間空間的感覺。生存期是時間,作用域是空間。
??無論是變量還是函數(shù),都要有一個名字。這個名字指的不是變量本身的值,而是變量存儲的地址。在訪問這個變量或函數(shù)的時候,根據(jù)地址去取值。(這里我有一個疑惑:C語言中指針即是地址,如果上面的說法正確的話,對于int a;來說,a是變量名,是元素a存儲的地址,a應(yīng)該是一個指針。那和 int *a有什么區(qū)別呢?)我對這個問題自己思考了一下:在int a中這個a并沒有從存儲空間的角度考慮,只是代表了一個變量名字而已。只需要明白可以通過地址找到變量值這個思想。
??調(diào)用函數(shù)的時候要傳遞參數(shù),在主調(diào)函數(shù)中的叫實(shí)參,被調(diào)函數(shù)中的叫形參。那 形參和實(shí)參間是如何實(shí)現(xiàn)傳遞的呢?主要有三種方式:傳地址、傳值、傳名。
??顧名思義,傳地址就是把參數(shù)地址送入被調(diào)函數(shù),被調(diào)函數(shù)可根據(jù)此地址進(jìn)行訪問,即被調(diào)函數(shù)要拿出空間A存放該地址B,實(shí)際使用時訪問A,取出B,按照B去拿C。(好繞!)。
??傳值是把參數(shù)值送入被調(diào)函數(shù),在被調(diào)函數(shù)中需要分配空間存儲參數(shù)值,到時候取值即可。傳名這個聽的時候比較少,這個就是在傳遞參數(shù)的時候,形參和實(shí)參之間有一一對應(yīng)的關(guān)系。前面兩種當(dāng)函數(shù)被調(diào)用的時候,內(nèi)容就已經(jīng)傳遞過去了。
??在傳名中,直到調(diào)用該形參的時候才會去對應(yīng)查找其實(shí)參。整體感覺有點(diǎn)像一個程序,使用時調(diào)用。所以也叫參數(shù)子程序。不過傳名幾乎不用,書上說只用于在ALGOL里面,了解一下就可以了。
??現(xiàn)在想一想,整個程序編譯運(yùn)行過程中。都要存儲哪些內(nèi)容?一是目標(biāo)代碼、二是數(shù)據(jù)信息、三是過程中調(diào)用的控制信息。其實(shí)還是比較好理解的,存儲生成的目標(biāo)代碼,機(jī)器才能一步一步的執(zhí)行指令進(jìn)行調(diào)用。數(shù)據(jù)信息包括各種變量、過程中的控制信息主要是指各個層次之間的嵌套關(guān)系,誰調(diào)用誰應(yīng)該心里有數(shù)。
??將以上三種內(nèi)容落實(shí)到具體就是活動記錄,下面介紹下活動記錄的定義:把過程的一個活動所需要的信息組織成一塊連續(xù)的存儲單元,稱為活動記錄。常以表格的方式來表示,以Pascal語言為例:它的活動記錄是下面這樣的:
??活動記錄大致可以分為三類:連接數(shù)據(jù)、形式單元、局部數(shù)據(jù)區(qū)。連接數(shù)據(jù)包括靜態(tài)鏈、動態(tài)鏈(后面會詳細(xì)介紹啥是動態(tài)鏈,靜態(tài)鏈)、返回地址。局部數(shù)據(jù)區(qū)包括臨時單元(中間代碼生成的),內(nèi)情向量(數(shù)組的首地址即大小范圍等信息),局部向量(子函數(shù)內(nèi)聲明的變量),形式單元(調(diào)用函數(shù)時傳遞的形參)。
??下面介紹下三種存儲分配策略:靜態(tài)存儲分配,棧式存儲分配,堆式存儲分配。其實(shí)理解這三者有一個很簡單的方式(當(dāng)然,簡單意味著不是很嚴(yán)謹(jǐn)~)靜態(tài)存儲分配就是指程序被預(yù)先分配的空間,沒有進(jìn)行動態(tài)分配,編譯時也不會附加任何空間。棧式存儲分配是系統(tǒng)自動進(jìn)行空間分配,最明顯的例子就是遞歸算法。程序結(jié)束,地址空間釋放。堆式存儲分配的特點(diǎn)是由用戶主動劃分,比如C++語言中的new函數(shù),同樣可以進(jìn)行歸還,也就是C++中的delete。下面更為詳細(xì)的介紹下三者:
靜態(tài)存儲分配:
??靜態(tài)存儲分配要滿足如下三個條件:
??在靜態(tài)存儲分配里包括一些聲明的變量空間的分配,在前面的文章中介紹過在做中間代碼生成的時候,會引入大量的中間變量(雖然在最后的優(yōu)化過程都會去除,但也要為其分配空間)。中間變量的大部分會被消除,如果為它們的每一個都分配空間顯得有點(diǎn)兒浪費(fèi),所以選擇了一種處理方式:
??有的時候,中間變量的生成之后,下一條語句就使用掉了,后面也不會再出現(xiàn),對于這種情況也設(shè)定了一種機(jī)制:
??實(shí)例如下:
棧式存儲分配
??關(guān)于棧式存儲分配只介紹了簡單的并且以C語言作為示范。棧式存儲分配都在棧空間里完成,所以需要進(jìn)行入棧管理。每個過程都以活動記錄作為基本單位,過程發(fā)生活動記錄入棧,過程結(jié)束出棧。
??那靜態(tài)存儲和棧式存儲有什么區(qū)別呢(棧式相比于靜態(tài))?
??舉一個抽象一點(diǎn)兒的例子:
??在這個例子中也不是說固定的子程序的活動記錄向上增長,這個取決于棧頂,不斷向下壓棧。
??那棧式空間中C語言的活動記錄是什么樣的呢?
??上圖的sp指的是stack pointer,即棧頂指針top。帶個老字就是上一層的。
??除了簡單的棧式實(shí)現(xiàn),還有一種是嵌套過程語言的棧式實(shí)現(xiàn)。什么叫嵌套過程呢?就是指在一個函數(shù)中,調(diào)用了另一個函數(shù)(該函數(shù)有可能繼續(xù)調(diào)用其他函數(shù),但不能相互調(diào)用引發(fā)死循環(huán),需要是合理的調(diào)用)。那這里有一個什么問題呢?就是變量使用的問題。在嵌套過程中,內(nèi)層是可以使用外層的變量的。有一種極端情況,內(nèi)外層變量名相同,這時會發(fā)生作用域的覆蓋。內(nèi)層調(diào)用的是內(nèi)層,會覆蓋掉外層。不考慮極端情況,在剛才的介紹中了解到每一層在棧中以活動記錄為單位進(jìn)行存儲,那內(nèi)層是如何訪問到外層的活動記錄呢?這部分就是主要解決這個問題的!
??解決的方式有兩種:靜態(tài)鏈或Display表。
??靜態(tài)鏈:
??靜態(tài)鏈的思想就是每層活動記錄保留上一層的sp,這樣如果想訪問外層變量就可以順著sp向上查找。這種方式固定增加了一個sp空間,是靜態(tài)的方式。其活動記錄如下:
??解釋下里面為啥有個靜態(tài)鏈,還有個動態(tài)鏈。靜態(tài)鏈就是我們剛才說的,用于進(jìn)行變量查詢。動態(tài)鏈就是之前棧式結(jié)構(gòu)中的老SP,用來表示調(diào)用關(guān)系的。
??用一個例子,來說明下:
??想想靜態(tài)鏈有什么問題嗎?如果嵌套了特別多層,最內(nèi)層想要調(diào)用最外層的變量需要一層一層向上查找。拿著必定會特別慢。有沒有什么方式可以加快速度呢?這就是Display表!
??Display表
??Display表的思想就是反正一層一層查找的目的就是為了引用外層變量,那我直接把所有的外層變量放在本層的活動記錄里不就可以嘛。這樣免得去其他層找。變量的個數(shù)不固定,導(dǎo)致表的空間大小不固定,所以也是動態(tài)分配的辦法,其活動記錄結(jié)構(gòu)如下:
??舉個例子(當(dāng)然使用標(biāo)序號的方式,活動記錄里都是序號了哈~):
??上圖里面有一項(xiàng)叫做:全局display,這個指的是上一層的display段開始部分對應(yīng)的標(biāo)號。活動記錄中display段的個數(shù)就反映了當(dāng)前的嵌套層數(shù)。第一層的全局display默認(rèn)為0。
??總結(jié)一下靜態(tài)鏈和Display表的優(yōu)缺點(diǎn):
??靜態(tài)鏈優(yōu)點(diǎn):空間固定,且占用空間小 缺點(diǎn):訪問速度太慢
??Display表優(yōu)點(diǎn):訪問速度快 缺點(diǎn):占用空間比較大
堆式動態(tài)分配
??在定義中使用“適當(dāng)大”來形容空間分配的大小,但實(shí)際上堆式分配的大小是由用戶指定。
??那如此多的空間,如何選出一個合適的呢?其實(shí)有三種策略:
??對于堆式分配,比較重要的一點(diǎn)就是內(nèi)存空間的釋放問題。如果不釋放的話,內(nèi)存就會被切分成小塊兒,導(dǎo)致以后使用的時候沒有足夠大的分配。這也告誡使用者在malloc之后加free,在new后加delete。
??內(nèi)存的分配始終是計算機(jī)科學(xué)的一個大的組成部分,如今在編譯原理中又學(xué)了一遍,漲了不少見識。但還是冰山一角,日后多多加油!
總結(jié)
- 上一篇: IAR 窗口重置默认配置
- 下一篇: 《论文笔记》Experimental R