C 堆記憶體 vs 棧記憶體 (2)

2021-08-20 14:51:10 字數 3576 閱讀 6857

第一部分了解了堆記憶體和棧記憶體的基本概念,以及值型別和引用了型別的記憶體分配,同時也了解了指標的基本情況。本節主要是介紹在函式呼叫時引數的傳遞情況。

在第一部分了解了函式呼叫時記憶體分配的基本情況,這一部分將對做進一步詳細介紹。當呼叫乙個函式時,會發生如下操作:

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 一般由程式設計師分配釋放,若程式設計師不...