內存堆和棧的區別

原文鏈接:http://www.cnblogs.com/lln7777/archive/2012/03/14/2396164.html

--------------------------------------------------------------------------------

在計算機領域,堆棧是一個不容忽視的概念,我們編寫的C語言程序基本上都要用到。但對于很多的初學著來說,堆棧是一個很模糊的概念。

堆棧:一種數據結構、一個在程序運行時用于存放的地方,這可能是很多初學者的認識,因為我曾經就是這么想的和匯編語言中的堆棧一詞混為一談。我身邊的一些編程的朋友以及在網上看帖遇到的朋友中有好多也說不清堆棧,所以我想有必要給大家分享一下我對堆棧的看法,有說的不對的地方請朋友們不吝賜教,這對于大家學習會有很大幫助。

?

數據結構的棧和堆


?

首先在數據結構上要知道堆棧,盡管我們這么稱呼它,但實際上堆棧是兩種數據結構:堆和棧。

堆和棧都是一種數據項按序排列的數據結構。

棧就像裝數據的桶或箱子

我們先從大家比較熟悉的棧說起吧,它是一種具有后進先出性質的數據結構,也就是說后存放的先取,先存放的后取。

這就如同我們要取出放在箱子里面底下的東西(放入的比較早的物體),我們首先要移開壓在它上面的物體(放入的比較晚的物體)。

堆像一棵倒過來的樹

  • 而堆就不同了,堆是一種經過排序的樹形數據結構,每個結點都有一個值。
  • 通常我們所說的堆的數據結構,是指二叉堆。
  • 堆的特點是根結點的值最小(或最大),且根結點的兩個子樹也是一個堆。

由于堆的這個特性,常用來實現優先隊列,堆的存取是隨意,這就如同我們在圖書館的書架上取書,雖然書的擺放是有順序的,但是我們想取任意一本時不必像棧一樣,先取出前面所有的書,書架這種機制不同于箱子,我們可以直接取出我們想要的書。

?

內存分配中的棧和堆


?

然而我要說的重點并不在這,我要說的堆和棧并不是數據結構的堆和棧,之所以要說數據結構的堆和棧是為了和后面我要說的堆區和棧區區別開來,請大家一定要注意。

下面就說說C語言程序內存分配中的堆和棧,這里有必要把內存分配也提一下,大家不要嫌我啰嗦,一般情況下程序存放在Rom(只讀內存,比如硬盤)或Flash中,運行時需要拷到RAM(隨機存儲器RAM)中執行,RAM會分別存儲不同的信息,如下圖所示:

?


?

內存中的棧區處于相對較高的地址以地址的增長方向為上的話,棧地址是向下增長的。

棧中分配局部變量空間,堆區是向上增長的用于分配程序員申請的內存空間。另外還有靜態區是分配靜態變量,全局變量空間的;只讀區是分配常量和程序代碼空間的;以及其他一些分區。

?

來看一個網上很流行的經典例子:

main.cpp?

復制代碼
 1 int a = 0; //全局初始化區 
2 char *p1; //全局未初始化區
3 main()
4 {
5 int b; //
6 char s[] = "abc"; //
7 char *p2; //
8 char *p3 = "123456"; //123456\0在常量區,p3在棧上。
9 static int c =0//全局(靜態)初始化區
10 p1 = (char *)malloc(10); //
11 p2 = (char *)malloc(20); //
12 }
復制代碼

?

0.申請方式和回收方式不同

不知道你是否有點明白了。

堆和棧的第一個區別就是申請方式不同:棧(英文名稱是stack)是系統自動分配空間的,例如我們定義一個 char a;系統會自動在棧上為其開辟空間。而堆(英文名稱是heap)則是程序員根據需要自己申請的空間,例如malloc(10);開辟十個字節的空間。

由于棧上的空間是自動分配自動回收的,所以棧上的數據的生存周期只是在函數的運行過程中,運行后就釋放掉,不可以再訪問。而堆上的數據只要程序員不釋放空間,就一直可以訪問到,不過缺點是一旦忘記釋放會造成內存泄露。還有其他的一些區別我認為網上的朋友總結的不錯這里轉述一下:

?

1.申請后系統的響應

:只要棧的剩余空間大于所申請空間,系統將為程序提供內存,否則將報異常提示棧溢出。

:首先應該知道操作系統有一個記錄空閑內存地址的鏈表,當系統收到程序的申請時,會遍歷該鏈表,尋找第一個空間大于所申請空間的堆結點,然后將該結點從空閑結點鏈表中刪除,并將該結點的空間分配給程序,另外,對于大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣,代碼中的 delete語句才能正確的釋放本內存空間。另外,由于找到的堆結點的大小不一定正好等于申請的大小,系統會自動的將多余的那部分重新放入空閑鏈表中。?

也就是說堆會在申請后還要做一些后續的工作這就會引出申請效率的問題。


2.申請效率的比較

根據第0點和第1點可知。

:由系統自動分配,速度較快。但程序員是無法控制的。

:是由new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便。

?

3.申請大小的限制

:在Windows下,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在 WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數),如果申請的空間超過棧的剩余空間時,將提示overflow。因此,能從棧獲得的空間較小。?

:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由于系統是用鏈表來存儲的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。

?

4.堆和棧中的存儲內容

由于棧的大小有限,所以用子函數還是有物理意義的,而不僅僅是邏輯意義。

: 在函數調用時,第一個進棧的是主函數中函數調用后的下一條指令(函數調用語句的下一條可執行語句)的地址,然后是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧的,然后是函數中的局部變量。注意靜態變量是不入棧的。?
當本次函數調用結束后,局部變量先出棧,然后是參數,最后棧頂指針指向最開始存的地址,也就是主函數中的下一條指令,程序由該點繼續運行。?

:一般是在堆的頭部用一個字節存放堆的大小。堆中的具體內容有程序員安排。

?

5.存取效率的比較

char s1[] = "aaaaaaaaaaaaaaa"; 
char *s2 = "bbbbbbbbbbbbbbbbb";

aaaaaaaaaaa是在運行時刻賦值的;放在棧中。?
而bbbbbbbbbbb是在編譯時就確定的;放在堆中。?
但是,在以后的存取中,在棧上的數組比指針所指向的字符串(例如堆)快。?

比如:?

復制代碼
#include 
void main()
{
char a = 1;
char c[] = "1234567890";
char *p ="1234567890";
a = c[1];
a = p[1];
return;
}
復制代碼

對應的匯編代碼?
10: a = c[1];?
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]?
0040106A 88 4D FC mov byte ptr [ebp-4],cl?
11: a = p[1];?
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]?
00401070 8A 42 01 mov al,byte ptr [edx+1]?
00401073 88 45 FC mov byte ptr [ebp-4],al


關于堆和棧區別的比喻

堆和棧的區別可以引用一位前輩的比喻來看出:?

使用棧就象我們去飯館里吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等準備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。?

使用堆就象是自己動手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由度大。比喻很形象,說的很通俗易懂,不知道你是否有點收獲。


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/538407.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/538407.shtml
英文地址,請注明出處:http://en.pswp.cn/news/538407.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MYSQL安裝和配置

Win10安裝MySQL5.7.22 解壓縮版(手動配置 1.下載地址:https://dev.mysql.com/downloads/mysql/5.7.html#downloads 直接點擊下載項 下載后: 2.可以把解壓的內容隨便放到一個目錄,我的是如下目錄(放到C盤的話&#xff0…

python刪除過期文件_python刪除過期文件的方法

本文實例講述了python刪除過期文件的方法。分享給大家供大家參考。具體實現方法如下:# remove all jpeg image files of an expired modification date mtime# you could also use creation date (ctime) or last access date (atime)# os.stat(filename) returns …

【很久之前的一篇老文章】一位程序員工作10年總結的13個忠告

展望未來,總結過去10年的程序員生涯,給程序員小弟弟小妹妹們的一些總結性忠告。 走過的路,回憶起來是那么曲折,把自己的一些心得體會分享給程序員兄弟姐妹們,雖然時代在變化,但是很可能你也會走我已經做過的…

apply()與call()的區別

一直都沒太明白apply()與call()的具體使用原理,今日閑來無事,決定好好研究一番。 JavaScript中的每一個Function對象都有一個apply()方法和一個call()方法,它們的語法分別為: /*apply()方法*/ function.apply(thisObj[, argArray]…

java代碼執行了兩次_Java中JComboBox的itemStateChanged事件執行兩次的解釋

今天做項目,用到了JComboBox,即下拉列表框。為了在被選中的項發生改變時獲得被選中的項,所以使用的ItemStateChanged事件,可是問題就來了,每次觸發該事件,它都執行兩次,屢試不爽。一開始以為是代…

python連接mongo_使用簡單的Python連接訪問MongoDB

繼續來聊MongoDB。MongoDB作為了一個數據庫產品軟件,除了服務器Server端進程(mongod)外,還提供了比較豐富的訪問連接接口。我們最常用的就是兩個類型,一個是原生mongo shell,另一個就是應用程序語言訪問接口。1、從Mongo Shell到應…

spring與mybatis三種整合方法

原文鏈接:http://www.cnblogs.com/wangmingshun/p/5674633.html ------------------------------------------------------------------------------------------------- 1、采用MapperScannerConfigurer,它將會查找類路徑下的映射器并自動將它們創建成…

js常用的2中排序方法:冒泡排序和快速排序

冒泡排序:例如9 4 5 6 8 3 2 7 10 1 首先:9和4比較 4放前 4 9 5 6 8 3 2 7 10 1 4和5比較 4不動 4 9 5 6 8 3 2 7 10 1 4和6比較 4不動 4 9 5 6 8 3 2 7 10 1 4和3比較 3放前 3 9 5 6 8 4 2 7 10 1 3和2比較 2放前 2 9 5 6 8…

java 注冊頁面正則式_Java使用正則表達式對注冊頁面進行驗證功能實現

本文給大家介紹java使用正則表達式對注冊頁面進行驗證的代碼,代碼如下所示:package regex;import java.util.Scanner;import java.util.regex.Matcher;import java.util.regex.Pattern;public class registered {public static void main(String[] args)…

python 編程效率_如何有效提升數據分析效率?五大Python技巧

如何有效提升數據分析效率?相信這是所有數據分析工作者都想解決的問題。本文整理了五大python技巧,分別是Pandas Profiling;使用 Cufflinks 和 Plotly 繪制 Pandas 數據;IPython 魔術命令;Jupyter 中的格式編排&#x…

please select a vaild python interpret

當 JetBrains PyCharm 2017.1.3 x64 遇到 please select a vaild python interpret 錯誤時: 進入PyCharm setting 選項,搜索 interpret

Grafana分析Nginx日志

配置Groub by -Terms時報錯,提示需要設置fielddatatrue,報錯內容大概如下: "Fielddata is disabled on text fields by default ... " 解決方法如下: https://www.elastic.co/guide/en/elasticsearch/reference/curren…

php curl json post請求_php post請求發送json對象數據參數

網頁中發送請求時,大部分情況都參數以鍵值組合發送數據的,而一些第三方如java開發的接口中需要發送post請求,請求參數為json類型。既然要發送json數據,首頁我們需要在請求頭中定義數據類型為json,告訴服務器客服端發送…

python刪除鏈表中的最小元素_LintCode Python 入門級題目 刪除鏈表元素、整數列表排序...

刪除鏈表元素:循環列表head,判斷當前指針pre.next的val是否等于val,如果是,當前pre重指向pre.next.next,直至pre.next Null# Definition for singly-linked list.# class ListNode:# def __init__(self, x):# self.va…

IDEA 更換主題

1、下載主題文件 百度或者谷歌 IDEA themes 網址有可能會變化。目前是 http://color-themes.com 選擇自己喜歡的顏色,下載。 2、導入主題文件 File----Import Setting 導入下載的jar文件,一路確認,idea會自動重啟。 3、選擇主題 點擊…

【CentOS 7筆記】cp、mv、文檔查看方式

2019獨角獸企業重金招聘Python工程師標準>>> 一. copy 常用cp -r/R #拷貝目錄,遞歸 cp -i #覆蓋時會提示,默認項 cp -p #保留源目錄或源文件的屬性 cp -b #源文目與目的文目建立鏈接,鏈接 cp -f #強制覆蓋 cp -v …

php 情書,php趣味編程 - php輸出笛卡爾情書的秘密

/*笛卡爾情書的秘密心形圖案的實現。重點是心形函數ra(1-sin),據說這是笛卡爾死前寄出的最后一封情書內容。這里面隱藏著一個刻骨銘心的秘密;“一生只為等待能手繪這個函數給我的人”*/$width 500;$height 500;header("Content-type: image/gif");$img …

python 月報_python實踐--月報分析之獲取jira缺陷數據

首先安裝jira,同其他第三方庫,直接可以 easy_install jira。判斷jira是否按轉成功輸入:from jira import JIRA,如果沒有報錯則說明安裝成功;#連接jirajira JIRA(“http://jira地址”,basic_auth (“用戶名…

JAVA中的native

native主要用于方法上,簡單介紹如下: 1、一個native方法就是一個Java調用非Java代碼的接口。一個native方法是指該方法的實現由非Java語言實現,比如用C或C實現。 2、在定義一個native方法時,并不提供實現體(比較像定…

script filename php,PHP $_SERVER['SCRIPT_FILENAME'] 與 __FILE__ 的區別

PHP $_SERVER[SCRIPT_FILENAME] 與 __FILE__通常情況下,PHP $_SERVER[SCRIPT_FILENAME] 與 __FILE__ 都會返回 PHP 文件的完整路徑(絕對路徑)與文件名:echo SCRIPT_FILENAME 為:,$_SERVER[SCRIPT_FILENAME];echo ;echo __FILE__ 為&#xff1…