C語言中動態記憶體的堆和棧

2021-06-06 03:31:02 字數 2297 閱讀 7385

一直有人抱怨動態記憶體的堆和棧到底有啥區別,具體到資料的存放,到底哪些放在堆裡,哪些放在棧中,還是放到了別的什麼地方,針對,大家不斷問到的問題,我也收集了一些資料,來和大家討論一下。

在c語言程式執行的過程中,需要記憶體來存放資料。這裡說的記憶體主要分為兩類:靜態的儲存區和動態的儲存區,如下圖所示:

其中,靜態資料儲存區分為三類:唯讀資料區(ro data), 已初始化的讀寫資料區(rw data)和未初始化讀寫資料區(bss)。這三類都是在程式的編譯-連線階段確定的,在程式執行階段是不會改變的,其大小和位置在程式執行過程中都是固定的。

而動態儲存區分為堆和棧。它們都是在程式執行的過程中動態分配的。其大小在程式執行的過程中將動態變化。在目前常見的體系結構和編譯系統中,一種典型的動態記憶體的管理形式為:棧記憶體從高向底位址分配,而堆記憶體則反過來。從記憶體管理實現的角度來看,堆使用鍊錶實現,而棧使用線性儲存的方式。棧是編譯器管理的。堆是由程式呼叫具體的庫函式管理的。

先來著重講棧:棧是先進後出,一般大家都會操作,這裡需要提醒的是,在實際處理的過程中,棧記憶體可能使用滿棧和空棧兩種情況,這是由處理器的體系結構決定的。在滿棧情況下,棧指標當前的位置是已經使用的棧區域;在空棧的情況下,棧指標當前的位置是沒有使用的棧區域。所以,按照滿棧處理的情況:在入棧的時候,要先移指標,後放入資料,出棧時,要先出資料,在移動指標。而按照空棧的出理方式,入棧時,先放入資料,在移動指標,出棧時,先移動指標,在出資料。這裡特別注意,是不一樣的。

需要說明的是,這些情況都是由處理器的硬體結構決定的,與程式的編寫沒有關係,甚至連編譯器都不知道這些事情。

在c語言中,體現棧空間使用的例子是引數的傳遞,返回值的使用以及自動變數的空間。引數進棧的順序是倒著來的,即在函式引數表中,先進後面的的,再進前邊的,為什麼呢?你這樣想,棧是從高位址空間向低位址空間遞減,把後面的先進去,就放在了高位址空間,這樣程式在按照順序訪問引數的時候,還是按照從低位址向高位址的順序訪問,這樣一來,訪問的時候就是按照參數列中的順序來訪問了。同時在棧中分配自動變數時,是和引數的宣告順序是一樣的。即先宣告的放在高位址空間中。

說完了棧,現在來說說堆。在一般的編譯系統中,堆記憶體的分配方向和棧記憶體是相反的(即從低到高),是通過呼叫c語言的庫函式完成的,是由使用者決定的,因此何時分配,何時釋放的邏輯完全使用程式的設計者來控制。從而出現了後面「談其色變」幾大問題:

1)記憶體洩漏:

申請了一塊記憶體,但是沒有釋放,結果程式結束了,它也沒有**,導致其他的程式要不能用。

2)野指標:

指乙個記憶體指標已經被釋放(通過free或者realloc),但是該指標依然在使用。那麼該怎麼做呢?正確的堆記憶體使用方法是當記憶體被釋放後,將記憶體的指標置為null,並在程式中要使用的時候判斷該記憶體指標是否為null,如果是null, 則認為內粗已經被釋放,不對記憶體進行訪問。

3)非法釋放指標:

從原則上講只有被malloc(),calloc()或realloc()分配並通過返回值返回返回的記憶體才能被釋放,否則釋放除此以外的記憶體都是非法的。即使有乙個指標是*p是malloc,那麼對p1=p++,這個時候free(p1)也是不合法的,但free(p)確實可以的。同樣釋放函式中的區域性變數也是非法的.還有一種情況是,對乙個堆記憶體釋放兩次也是錯誤的用法。因為free()函式是不能釋放未分配的堆記憶體。在程式使用free釋放記憶體之後,應該將指標置為null,free乙個null位址是沒有問題的。另外,如果釋放的是從函式引數傳入的指標,由於無法通知外部這個指標已經被釋放,在別的地方如果指標再次被釋放,就會發生多次釋放同乙個指標的錯誤。

上面說了那麼多,那麼堆和棧綜合起來比較有那些不同呢?我們說,函式的返回值是儲存在棧上的,但返回值可以是乙個指標,它可以指向靜態儲存區,可以指向堆記憶體的位址,同樣也可以指向函式呼叫者的棧空間(主調函式),但是不能指向乙個函式內部棧記憶體區域的位址(被調函式)。

另外乙個需要說明的問題是,函式引數同樣可是陣列,但值得注意的是陣列作為引數使用的時候,將當作指標處理,而不會像結構體那樣把整個內容全部壓入棧,實際入棧的僅僅是乙個陣列位址的指標。如把int a[100]作為引數的時候,並沒有將陣列的100個元素全部入棧,,而是把陣列的指標a放入了棧,實際處理過程與指標是相同的。即void func(int a[100])和void func(int* a)這兩種處理方式是一樣的。這也說明陣列的sizeof()運算將返回陣列的大小,同樣陣列名是不能進行自增自減操作的。然而,對於引數中的陣列,這個時候是可以進行自增減以及其它賦值操作,對它進行sizeof將永遠返回指標的大小4位元組(因為剛說了,在函式引數中,他就被當成個個指標處理)。

C語言中棧和堆 記憶體

棧的特點 是 速度快,儲存小,自動釋放記憶體 比如函式 堆的特點是,速度稍慢於棧,儲存空間大,手動釋放記憶體,呼叫free函式 比如malloc定義大量儲存空間,如果 不釋放記憶體,將一直占用記憶體空間,直到退出程式。記憶體洩漏也稱作 儲存滲漏 用動態儲存分配函式動態開闢的空間,在使用完畢後未釋放,...

C語言中動態記憶體分配

c語言用了蠻久了,最近在寫乙個dsp的程式,發現動態記憶體使用這一塊還是很欠缺,於是又重新看了看c的書,總結一下。之前常見的陣列或結構體內存分配,其長度必須是固定的常數,如 int a 10 等,當需要申請變長陣列時,常規的直接定義就不可以了,如float b n 其中n為變數 好像c99之後可以這...

c語言中的動態記憶體管理

malloc calloc realloc的區別與聯絡 在c語言 c 中和記憶體申請與釋放的相關函式有 alloc malloc calloc realloc free.1 alloc是在棧上申請空間,無需使用者主動釋放,當函式執行結束時,在棧上分配的記憶體會被自動釋放,棧記憶體分配運算內置於處理器...