1. 程式人生 > >靜態儲存區、堆、棧之間的區別

靜態儲存區、堆、棧之間的區別

一、記憶體基本構成

可程式設計記憶體在基本上分為這樣的幾大部分:靜態儲存區、堆區和棧區。他們的功能不同,對他們使用方式也就不同。

靜態儲存區:記憶體在程式編譯的時候就已經分配好,這塊記憶體在程式的整個執行期間都存在。它主要存放靜態資料、全域性資料和常量。

棧區:在執行函式時,函式內區域性變數的儲存單元都可以在棧上建立,函式執行結束時這些儲存單元自動被釋放。棧記憶體分配運算內置於處理器的指令集中,效率很高,但是分配的記憶體容量有限。

堆區:亦稱動態記憶體分配。程式在執行的時候用malloc或new申請任意大小的記憶體,程式設計師自己負責在適當的時候用free或delete釋放記憶體。動態記憶體的生存期可以由我們決定,如果我們不釋放記憶體,程式將在最後才釋放掉動態記憶體。 但是,良好的程式設計習慣是:如果某動態記憶體不再使用,需要將其釋放掉,否則,我們認為發生了記憶體洩漏現象。

二、三者之間的區別

我們通過程式碼段來看看對這樣的三部分記憶體需要怎樣的操作和不同,以及應該注意怎樣的地方。

例一:靜態儲存區與棧區

char* p = “Hello World1”;

char a[] = “Hello World2”;

p[2] = ‘A’;

a[2] = ‘A’;

char* p1 = “Hello World1;”

C++ 靜態儲存區 棧 堆的區別[網摘] - chen_hans - Seа

這個程式是有錯誤的,錯誤發生在p[2] = ‘A’這行程式碼處,為什麼呢,是變數p和變數陣列a都存在於棧區的(任何臨時變數都是處於棧區的,包括在main()函式中定義的變數)。但是,資料“Hello World1”和資料“Hello World2”是儲存於不同的區域的。

因為資料“Hello World2”存在於陣列中,所以,此資料儲存於棧區,對它修改是沒有任何問題的。因為指標變數p僅僅能夠儲存某個儲存空間的地址,資料“Hello World1”為字串常量,所以儲存在靜態儲存區。雖然通過p[2]可以訪問到靜態儲存區中的第三個資料單元,即字元‘l’所在的儲存的單元。但是因為資料“Hello World1”為字串常量,不可以改變,所以在程式執行時,會報告記憶體錯誤。並且,如果此時對p和p1輸出的時候會發現p和p1裡面儲存的地址是完全相同的。換句話說,在資料區只保留一份相同的資料(見圖1-1)。

例二:棧區與堆區

char* f1()

{

char* p = NULL;

char a;

p = &a;

return p;

}

char* f2()

{

char* p = NULL:

p =(char*) new char[4];

return p;

}

這兩個函式都是將某個儲存空間的地址返回,二者有何區別呢?f1()函式雖然返回的是一個儲存空間,但是此空間為臨時空間。也就是說,此空間只有短暫的生命週期,它的生命週期在函式f1()呼叫結束時,也就失去了它的生命價值,即:此空間被釋放掉。所以,當呼叫f1()函式時,如果程式中有下面的語句:

char* p ;

p = f1();

*p = ‘a’;

此時,編譯並不會報告錯誤,但是在程式執行時,會發生異常錯誤。因為,你對不應該操作的記憶體(即,已經釋放掉的儲存空間)進行了操作。但是,相比之下,f2()函式不會有任何問題。因為,new這個命令是在堆中申請儲存空間,一旦申請成功,除非你將其delete或者程式終結,這塊記憶體將一直存在。也可以這樣理解,堆記憶體是共享單元,能夠被多個函式共同訪問。如果你需要有多個數據返回卻苦無辦法,堆記憶體將是一個很好的選擇。但是一定要避免下面的事情發生:

void f()

{

char * p;

p = (char*)new char[100];

}

這個程式做了一件很無意義並且會帶來很大危害的事情。因為,雖然申請了堆記憶體,p儲存了堆記憶體的首地址。但是,此變數是臨時變數,當函式呼叫結束時p變數消失。也就是說,再也沒有變數儲存這塊堆記憶體的首地址,我們將永遠無法再使用那塊堆記憶體了。但是,這塊堆記憶體卻一直標識被你所使用(因為沒有到程式結束,你也沒有將其delete,所以這塊堆記憶體一直被標識擁有者是當前您的程式),進而其他程序或程式無法使用。我們將這種不道德的“流氓行為”(我們不用,卻也不讓別人使用)稱為記憶體洩漏。這是我們C++程式設計師的大忌!!請大家一定要避免這件事情的發生。

總之,對於堆區、棧區和靜態儲存區它們之間最大的不同在於,棧的生命週期很短暫。但是堆區和靜態儲存區的生命週期相當於與程式的生命同時存在(如果您不在程式執行中間將堆記憶體delete的話),我們將這種變數或資料成為全域性變數或資料。但是,對於堆區的記憶體空間使用更加靈活,因為它允許你在不需要它的時候,隨時將它釋放掉,而靜態儲存區將一直存在於程式的整個生命週期中。

希望大家記住下面的規則:

      【規則1】用malloc 或new 申請記憶體之後,應該立即檢查指標值是否為NULL。防止使用指標值為NULL 的記憶體。
     【規則2】不要忘記為陣列和動態記憶體賦初值。防止將未被初始化的記憶體作為右值使用。
     【規則3】避免陣列或指標的下標越界,特別要當心發生“多1”或者“少1”操作。
     【規則4】動態記憶體的申請與釋放必須配對,防止記憶體洩漏。
     【規則5】用free 或delete 釋放了記憶體之後,立即將指標設定為NULL,防止產生“野指標”。

堆與棧的討論:
管理方式:
   堆中資源由程式設計師控制(容易產生memory leak)。
   棧資源由編譯器自動管理,無需手工控制。

系統響應:
   對於堆,應知道系統有一個記錄空閒記憶體地址的連結串列,當系統收到程式申請時,遍歷該連結串列,尋找第一個空間大於申請空間的堆結點,刪除空閒結點連結串列中的該結點,並將該結點空間分配給程式(大多數系統會在這塊記憶體空間首地址記錄本次分配的大小,這樣delete才能正確釋放本記憶體空間,另外系統會將多餘的部分重新放入空閒連結串列中)。
   對於棧,只要棧的剩餘空間大於所申請空間,系統為程式提供記憶體,否則報異常提示棧溢位。

空間大小:
   堆是不連續的記憶體區域(因為系統是用連結串列來儲存空閒記憶體地址,自然不是連續的),堆大小受限於計算機系統中有效的虛擬記憶體(32bit系統理論上是4G),所以堆的空間比較靈活,比較大。
   棧是一塊連續的記憶體區域,大小是作業系統預定好的,windows下棧大小是2M(也有是1M,在編譯時確定,VC中可設定)。

碎片問題:
   對於堆,頻繁的new/delete會造成大量碎片,使程式效率降低。
   對於棧,它是一個先進後出的佇列,進出一一對應,不會產生碎片。

生長方向:
   堆向上,向高地址方向增長。
   棧向下,向低地址方向增長。

分配方式:
   堆都是動態分配(沒有靜態分配的堆)。
   棧有靜態分配和動態分配,靜態分配由編譯器完成(如區域性變數分配),動態分配由alloca函式分配,但棧的動態分配的資源由編譯器進行釋放,無需程式設計師實現。

分配效率:
   堆由C/C++函式庫提供,機制很複雜。所以堆的效率比棧低很多。
   棧是極其系統提供的資料結構,計算機在底層對棧提供支援,分配專門暫存器存放棧地址,棧操作有專門指令

相關推薦

靜態儲存區之間區別

一、記憶體基本構成可程式設計記憶體在基本上分為這樣的幾大部分:靜態儲存區、堆區和棧區。他們的功能不同,對他們使用方式也就不同。靜態儲存區:記憶體在程式編譯的時候就已經分配好,這塊記憶體在程式的整個執行期間都存在。它主要存放靜態資料、全域性資料和常量。棧區:在執行函式時,函式內

C語言程式記憶體中:靜態儲存區區別

一、記憶體基本構成 可程式設計記憶體在基本上分為這樣的幾大部分:靜態儲存區、堆區和棧區。他們的功能不同,對他們使用方式也就不同。 靜態儲存區:記憶體在程式編譯的時候就已經分配好,這塊記憶體在程式的整個執行期間都存在。它主要存放靜態資料、全域性資料和常量。 棧區:在執行函式時,函式內區域性變數的儲存單元都可以在

轉---隊列區別

都是 fix 包括 article manage class alloc 速度 -a 隊列、堆、棧、堆棧的區別 堆棧:先進後出(就像放在箱子的衣服,先放進去的後拿出來) 隊列:先進先出(就像一條路,有一個入口和一個出口,先進去的就可以先出去)   進程

和佇列堆疊的區別

佇列是先進先出,有出口和入口,先進去可以先出來。棧就像一個箱子,後放上去的,可以先出來堆是在程式執行時,而不是在程式編譯時,申請某個大小的記憶體空間。即動態分配記憶體,對其訪問和對一般記憶體的訪問沒有區別。{堆是指程式執行是申請的動態記憶體,而棧只是指一種使用堆的方法(即先進

堆疊佇列,它們之間的關係

堆疊和棧就是一個概念!!! 佇列是先進先出:就像一條路,有一個入口和一個出口,先進去的就可以先出去。 而棧就像一個箱子,後放的在上邊,所以後進先出。 (堆和它們不同,不存在是先進後出還是先進先出) 1.棧(Stack)是作業系統在建立某個程序時或者執行緒(在支援多執行緒

1.1JVM記憶體結構——方法區直接記憶體區別

一、定義 1、堆:FIFO佇列優先,先進先出。jvm只有一個堆區被所有執行緒所共享!堆存放在二級快取中,呼叫物件的速度相對慢一些,生命週期由虛擬機器的垃圾回收機制定。2、棧:FILO先進後出,暫存資料的地方。每個執行緒都包含一個棧區!棧存放在一級快取中,存取速度較快,“棧是限

方法區直接記憶體區別

       新生區是類的誕生、成長、消亡的區域,一個類在這裡產生,應用,最後被垃圾回收器收集,結束生命。新生區又分為兩部分:伊甸區(Eden space)和倖存者區(Survivor pace),所有的類都是在伊甸區被new出來的。倖存區有兩個:0區(Survivor 0 space)和1區(Survivo

佇列堆疊的區別

*************************************************************************************************************************************程序中每個執行緒都有自己的堆疊,這是一段執行

記憶體區劃分記憶體分配常量儲存區自由儲存區全域性區[C++][記憶體管理][轉載]

一. 在c中分為這幾個儲存區 1.棧 - 由編譯器自動分配釋放 2.堆 - 一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由OS回收 3.全域性區(靜態區),全域性變數和靜態變數的儲存是放在一塊的,初始化的全域性變數和靜態變數在一塊區域,未初始化的全域性變數和未初始化的靜態變數在相鄰的另一

堆疊區別

堆和棧的區別 一、預備知識—程式的記憶體分配 一個由c/C++編譯的程式佔用的記憶體分為以下幾個部分 1、棧區(stack)— 由編譯器自動分配釋放 ,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。 2、堆區(heap) — 一般由程式設計師分配釋放, 若程式設計師不釋放,程式結束時可能

資料型別(基本資料型別引用資料型別)以及區別

js資料型別:基本資料型別和引用資料型別(文章最下面會介紹各型別的基礎以及注意事項) 基本資料型別指的是簡單的資料段,引用資料型別指的是有多個值構成的物件 當我們把變數賦值給一個變數時,解析器首先要確認的就是這個值是基本型別值還是引用型別值 基本資料型別:數字(Numb

區域性變數全域性變數堆疊靜態和全域性的區別

一般全域性變數存放在資料區,區域性變數存放在棧區,動態變數存放在堆區,函式程式碼放在程式碼區。 棧區是普通的棧資料結構,遵循LIFO後進先出的規則,區域性變數安排在那裡是ASM時就規定的,這樣可以在一

StringStringBuffer與StringBuilder之間區別 .RP

什麽 方式 tr1 abcd 為什麽 mes strong 速度 一點   最近學習到StringBuffer,心中有好些疑問,搜索了一些關於String,StringBuffer,StringBuilder的東西,現在整理一下。 關於這三個類在字符串處理中的位置不言而喻,

StringStringBuffer與StringBuilder之間區別

安全 .com 學習 ron img build tro ges 變量   大家在最初結束String字符串的時候,都會被教做認為String是不可變的字符串常量,是不可改變的常量。但是我們看下面的一個列子:   為什麽會發生這種情況呢?難道最開始我們學習的就是錯誤的?

自己(轉)StringStringBuffer與StringBuilder之間區別

理解 疑問 多線程 blog gpo string類 body 對象 就是   最近學習到StringBuffer,心中有好些疑問,搜索了一些關於String,StringBuffer,StringBuilder的東西,現在整理一下。 關於這三個類在字符串處理中的位置不言

Java 中的 JVM -- 初步了解

eap 調用 程序 mmm 劃分 創建 都是 分配 2015a JVM -- Java Virtual Machine(Java虛擬機)   —— 因為要說堆和棧,所以我們必須要先簡單的說一下JVM。(JVM詳細請找度娘啦~)   首先,我們都知道 java 一直宣傳的口號

程序的段

.data 初始化 申請 font har lin -h tom 才會 1.程序就是編譯出來的鏡像,處於執行狀態的程序叫進程.一個程序可以執行多次,每次執行會產生一個進程. 2. 程序鏡像裏有分成很多個段: 段其實就是在程序鏡像文件裏從一個位置到另一個位置範圍裏存放某種

理解Java虛擬機中的

線程 常量 9.png lan 和數 編譯器 對象 堆棧 棧區 JAVA的JVM的內存可分為3個區:堆(heap)、棧(stack)和方法區(method) 棧區: 每個線程包含一個棧區,棧中只保存方法中(不包括對象的成員變量)的基礎數據類型和自定義對象的引用

“吃人”的那些Java名詞:物件引用

記得中學的課本上,有一篇名為《狂人日記》課文;那時候根本理解不了魯迅寫這篇文章要表達的中心思想,只覺得滿篇的“吃人”令人心情壓抑;老師在講臺上慷慨激昂的講,大多數的同學同我一樣,在課本面前“痴痴”的發呆。 作為一個有著8年Java程式設計經驗的IT老兵,說起來很慚愧,我被Java當中的四五個名詞一直困擾著: