第一部分了解了堆記憶體和棧記憶體的基本概念,以及值型別和引用了型別的記憶體分配,同時也了解了指標的基本情況。本節主要是介紹在函式呼叫時引數的傳遞情況。
在第一部分了解了函式呼叫時記憶體分配的基本情況,這一部分將對做進一步詳細介紹。當呼叫乙個函式時,會發生如下操作:
1. 在棧記憶體上為呼叫的方法分配空間,這裡主要包括返回位址指標和引數傳遞所需空間。
2. 複製方法引數,這是將要詳細研究的部分。
3. 指令指標指向被調函式的jit**,然後開始執行**,因此在堆疊上又分配了乙個棧幀。
**:
public
intaddfive(int pvalue)
呼叫該函式時,堆疊類似下面:
在第一部分中討論過,引數記憶體的分配與引數是值型別還是引用型別有關,值型別直接進行拷貝,而引用型別引數拷貝引數的引用(指標)。
首先,當傳遞值型別引數時,會在棧記憶體上分配空間,並將值複製到分配的空間,通過下面例子看下具體情況。
public
class class1
public
intaddfive(int pvalue)
}
當呼叫 「go」 方法時,在棧記憶體上為區域性變數 x 分配空間,並將值 5 拷貝到分配的空間。棧記憶體變化如下圖:
接下來,當呼叫 addfive 時,記錄函式的返回位址,並為addfive所需記憶體(區域性變數、引數)並將值複製到分配的位址。
當 addfive 呼叫結束之後,根據上一步記錄的返回位址,返回到go函式中;並且將 addfive呼叫時分配的棧記憶體空間進行釋放。
所以最終 x 的值為5,主要原因在於,所有值型別引數在傳遞時都是傳遞的副本,而非直接修改原始值。基於這一點,我們需要知道,當傳遞乙個非常大的值型別時(比如乙個struct),每次複製都非常的消耗空間和時間,棧記憶體空間是有限的,所以在使用struct時,需要慎重考慮。
通過乙個例子來看一下結構體做引數時的情況:
public
struct mystruct
public
class program
public
void
dosomething(mystruct pvalue)
}
當執行 go() 方法時,棧記憶體會發生如下變化:
這種方式是非常低效的,所以一種更好的替代方法是,通過傳引用的方式傳遞結構體引數,下面對上面**進行下修改:
public
class program
public
void
dosomething(ref mystruct pvalue)
}
此種方式減少了不必要記憶體的消耗,呼叫 go 時棧記憶體變化如下:
當選擇傳值或者按引用傳遞時,需要注意物件值得變化,區分清楚再函式內修改物件時副本還是物件本身。
傳遞引用型別引數,和按引用傳遞值型別引數時相似的。
public
class myint
public
class program
}
當執行 go() 函式時,堆疊記憶體變化如下:
下面修改一下 go 函式的內容
public
void
go()
public
void
dosomething(myint pvalue)
此時在呼叫 go 函式時,堆疊變化如下:
具體過程如下:
1. 在go 方法開始,在堆記憶體上為 x 分配記憶體,並將記憶體位址複製到棧記憶體。
2. 在開始呼叫 dosomething 時,引數pvalue 的將在棧記憶體進行分配。
3. x 物件的指標值複製到 pvalue記憶體。
所以當在 dosomething 函式中修改myint屬性myvalue時,修改的其實是堆記憶體上的內容,所以在呼叫console.writeline() 時輸出內容是:12345
下面研究一下按引用傳遞引用型別時會發生什麼,看下下面的**。
public
class thing{}
public
class animal:thing{}
public
class vegetable:thing{}
public
void
go()
public
void
switcharoo(ref thing pvalue)
輸出結果:
x is animal : false
x is vegetable : true
看下堆疊記憶體發生了什麼變化:
具體步驟:
1. 開始呼叫 go 函式時,在棧內存在 x 指標分配空間。
2. 然後將 animal 物件的位址,複製到 x 指標位址。
3. 開始呼叫 switchroo 函式,將 x 的位址複製到 pvalue 記憶體中。
4. 在 堆記憶體 上為 vegetable 分配記憶體。
5. 利用 pvalue,使 x 指向剛分配的 vegetable 記憶體位址。
大致總結一下這一部分內容:
1. 傳遞值型別引數時,將實參的副本傳遞給函式。
2. 傳遞引用型別引數時,將引用物件位址傳遞給函式。
3. 當按引用傳遞引用型別引數時,將棧記憶體位址(儲存著物件在堆記憶體的位址),傳遞給函式。
通過本節介紹,對於記憶體如何處理引數傳遞,應該有了乙個比較直觀的理解,直到如何處理一些引數傳遞相關問題。
C 棧 堆記憶體
由編譯器自動分配釋放 存放函式引數值,區域性變數值等。其操作方式類似於資料結構中棧。一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由os 注意它與資料結構中堆是兩回事,分配方式倒是類似於鍊錶,呵呵。全域性變數和靜態變數儲存是放在一塊,初始化全域性變數和靜態變數在一塊區域,未初始化全域性...
堆記憶體 棧記憶體
從堆和棧的功能和作用來通俗的比較,堆主要用來存放物件的,棧主要是用來執行程式的.而這種不同又主要是由於堆和棧的特點決定的 在程式設計中,例如c c 中,所有的方法呼叫都是通過棧來進行的,所有的區域性變數,形式引數都是從棧中分配記憶體空間的。實際上也不是什麼分配,只是從棧頂向上用就行,就好像工廠中的傳...
C 堆記憶體和棧記憶體詳解
堆 順序隨意 棧 先進後出 堆和棧的區別 一 預備知識 程式的記憶體分配 乙個由c c 編譯的程式占用的記憶體分為以下幾個部分 1 棧區 stack 由編譯器自動分配釋放 存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧 2 堆區 heap 一般由程式設計師分配釋放,若程式設計師不...