編碼完成之後,不論它能否正確的達到我們預期的目標,首先都需要通過編譯器的編譯,也就是說我們的編碼至少是在文法上是正確的。所謂文法的正確就是我們的代碼要符合所用語言的文法規定,其實質是使得編譯器能夠正確的解析,從而得到目標代碼在機器上執行。
有時候,通過編譯僅僅是一個開始,我們會發現程式的結果同我們的預想往往有較大差異。拋開演算法本身的問題不說,一些編程細節的疏忽導致的問題足以讓我們百思不得其解,著急、鬱悶卻有無可奈何。例如,參數傳遞問題,變數範圍問題,返回局部變數等等問題,一直在困擾著我們。即使在網上看到過一些相關文章講解堆、棧等等知識,但仍然經常犯糊塗,貌似其竟是如此神秘。
如果我們能夠知道編譯器是如何工作的,那麼對這些問題的認識就會上升一個層次,許多困擾我們的不解與疑問或許就會迎刃而解,豁然開朗。因此,我們不妨抽出一段時間,重溫一下似乎早已遺忘的編譯原理教材吧!
編譯器的記憶體配置策略
假定編譯器從作業系統得到一Block Storage區,用於被編譯的程式運行。該儲存區將被分塊,用來儲存:
1. 產生的目標代碼
2. 待用資料
3. 記錄程式調用的控制棧
4. 堆
程式每次執行時所需的資訊都用一塊連續的儲存區來管理,稱作活動記錄。其中包括了以下資訊域:傳回值,實參,控制鏈(可選),訪問鏈(可選),儲存的機器狀態,局部資料和臨時資料。
其中,傳回值儲存被調用程式返回給調用程式的值;實參域儲存調用函數傳給被調用函數的參數值;控制鏈指向調用者的活動紀錄;訪問鏈用來應用其他活動記錄中的非局部資料;激起狀態域儲存程式調用前機器的狀態資訊,如:程式計數器的值和各種寄存器的值等等;局部資料域儲存局部於程式執行的資料;臨時資料儲存如計算運算式的中間結果這樣的臨時資料。
編譯器一般有三種記憶體配置策略:
1. 靜態分配
2. 棧式分配
3. 堆式分配
靜態分配,變數在編譯時間就確定了儲存空間,靜態變數的值被綁定到固定的儲存空間,因此每次函數調用後局部於函數的靜態變數仍然綁定到同樣的儲存單元,即使調用完成,局部變數的值仍然儲存。
棧式分配,把儲存空間組織為棧,而且隨著程式調用的開始和結束進行進棧和出棧。每次調用都將一個活動記錄壓入堆棧,調用結束時彈棧。堆式分配,把連續的儲存區分成塊,當活動記錄或其他對象需要時就分配。塊的釋放可以按照任意順序進行。注意,這部分不是由編譯器自動釋放的,必須由程式員來處理。
瞭解了編譯器的記憶體配置方式,我們就可以理解一些經常遇到的問題的原因。
返回局部變數的引用
過程每次調用時,局部變數的儲存空間都包含在每次調用的活動紀錄中。由於每次調用都引起新的活動記錄進棧,所以每次調用時,局部變數都分配到不同的儲存單元。而且在活動記錄彈棧的時候所有局部變數的儲存空間將被釋放,儲存的值將被刪除。
參數傳遞(按值傳遞,按指標傳遞,按引用傳遞)
參數傳遞方法之間的區別主要是基於實參代表的是左值、右值還是實參的本文本身(待續)
本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/mwsong/archive/2006/08/05/1025239.aspx