注意:
①咱們有時候會遇到「堆棧」這種稱呼,根據語義「堆棧」這種稱呼無非等價如下兩種說法:html
②操做系統的堆和棧是指對內存進行操做和管理的一些方式這和數據結構中的堆和棧是有區別的。java
在計算機系統中,棧也能夠稱之爲棧內存是一個具備動態內存區域,存儲函數內部(包括main函數)的局部變量和方法調用和函數參數值。咱們須要知道:node
①內存管理:
棧由編譯器自動分配釋放。
②棧中的內容存放
棧存放函數的參數值,局部變量的值等。其操做方式相似於數據結構中的棧。知足:「先進後出」的原則存取,也就是位於棧內的元素,必須等到其上面(對應的地址爲較低的地址)的數據或函數執行完成後,彈出後才能夠進行下面的元素的操做。
③棧的結構:
棧的一端是固定的,另外一端是浮動的。固定的一端是較高的地址,咱們稱棧頂。浮動的一端是較低的地址(最低到0)隨着元素或方法的加入棧頂指針會逐步地往下移動。全部的數據存入或取出,只能在浮動的一端(稱棧頂)進行。(棧的生長方向是向下的,是向着內存地址減少的方向增加)
④棧的速度:
棧是由系統自動分配的,通常速度較快(棧的速度高於堆的速度)
⑤申請大小的限制:
棧是向低地址擴展的,是一塊連續的內存的區域。是棧頂的地址和棧的最大容量是系統預先規定好的,棧的大小是2M(也有的說是1M,總之是一個編譯時就肯定的常數 ) ,若是申請的空間超過棧的剩餘空間時,將提示overflow。所以,能從棧得到的空間較小。
⑥單片機中的棧:單片機應用中,棧是個特殊存儲區,棧屬於RAM空間的一部分,棧用於函數調用、中斷切換時保存和恢復現場數據。棧中定義了一些操做,兩個最重要的是PUSH和POP。 PUSH(入棧)操做:堆棧指針(SP)加1,而後在堆棧的頂部加入一 個元素。POP(出棧)操做相反,出棧則先將SP所指示的內部ram單元中內容送入直接地址尋址的單元中(目的位置),而後再將堆棧指針(SP)減1。這兩種操做實現了數據項的插入和刪除。程序員
函數的每一次調用,均會在調用棧(call stack)上維護一個獨立的棧幀(stack frame).每一個獨立的棧幀通常包括:
①函數的返回地址和參數。
②臨時變量: 包括函數的非靜態局部變量以及編譯器自動生成的其餘臨時變量。
③函數調用的上下文。
棧是從高地址向低地址延伸,一個函數的棧幀用ebp和esp這兩個寄存器來劃定範圍.ebp 指向當前的棧幀的底部,esp 始終指向棧幀的頂部;
ebp 寄存器又被稱爲幀指針(Frame Pointer);
esp 寄存器又被稱爲棧指針(Stack Pointer);算法
下面以函數P內部調用函數Q爲例:segmentfault
①在函數P調用函數Q的過程,Q在執行時,P以及全部在向上追溯到P的調用鏈中的過程,都是被暫時掛起的。
②當Q運行時,它只須要爲局部變量分配新的儲存空間,或者設置另外一個過程的調用。
③Q返回時,任何它所分配的局部存儲空間均可以被釋放。當P調用Q時控制和數據信息會添加到棧尾,當P返回時,這些信息會被釋放掉。
對於調用中棧幀的過程:
①當前正在執行的棧幀老是在棧頂,當過程P調用過程Q時,會把返回地址壓入棧中,指明當Q返回時,要從P程序的哪一個位置繼續執行。而且這個返回地址看成P的棧幀的一部分。由於它存放的是與P相關的狀態。
②Q的代碼會推展當前棧的邊界,並分配它的棧幀所需的空間。這個空間中,Q能夠保存寄存器的值,分配局部變量空間,爲它調用的過程分配參數
注意:並非全部函數都學要棧幀的,好比不調用任何子函數的函數。數組
①堆的內存管理:
堆是程序中一塊預留的內存空間,可由程序自由使用,堆被程序申請使用的內存在被主動釋放前一直有效。通常由程序員分配釋放, 若程序員不釋放,對於堆來說,釋放工做由程序員手動管理,不及時回收容易產生內存泄露。 程序結束時可能由操做系統回收。
②C中對空間的申請:C語言程序中經過頭文件爲「malloc.h」的庫函數調用得到堆空間,關鍵字「malloc」以字節的方式動態申請堆空間,關鍵字「free」將堆空間歸還給系統。
③堆的速度:有種說法是「棧是存放在一級緩存中的,而堆則是存放在二級緩存中的,堆的生命週期由虛擬機的垃圾回收算法來決定(並非一旦成爲孤兒對象就能被回收)。」因此調用這些對象的速度要相對來得低一些,故堆的速度慢於棧的速度。
④系統對堆空間的管理方式:咱們瞭解下空閒鏈表法這種管理方式::首先應該知道操做系統有一個記錄空閒內存地址的鏈表,當系統收到程序的申請時,會遍歷該鏈表,尋找第一個空間大於所申請空間的堆結點,而後將該結點從空閒結點鏈表中刪除,並將該結點的空間分配給程序,另外,對於大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本內存空間。另外,因爲找到的堆結點的大小不必定正好等於申請的大小,系統會自動的將多餘的那部分從新放入空閒鏈表中。
下部分描述參見:程序中的三國天下:棧、堆、靜態存儲區:
分析:
假設每一個節點下的內存都爲 12byte、100byte、50byte....
當申請內存時,系統遍歷空閒鏈表節點,查看所需內存大小與哪個節點下內存大小最接近
到此節點下查找可用單元,查找到以後返回對應單元地址
在空閒鏈表管理法中,存在查找所需內存大小與哪一節點最接近的操做,這將致使 malloc 實際分配的內存可能會比請求的多。但咱們不能依賴這種行爲,由於在不一樣的系統中,對堆空間的管理方式多是不一樣的。緩存
①按分配方式分:
棧:棧有兩種分配方式:靜態分配和動態分配
堆:在C中堆是動態分配和回收內存的,沒有靜態分配的堆。
(靜態分配是系統編譯器完成的,好比局部變量的分配.)
(動態分配是有malloc函數進行分配的,可是棧的動態分配和堆是不一樣的,它的動態分配也由系統編譯器進行釋放,不須要程序員手動管理)
②申請大小的限制:
棧:棧是向低地址擴展的數據結構,是一塊連續的內存的區域。
堆:堆是向高地址擴展的數據結構,是不連續的內存區域。堆得到的空間比較靈活,也比較大。
③效率:
棧:由系統自動分配,速度較快,不會產生內存碎片。
堆:在C中堆是由malloc分配的內存,速度比較慢,並且容易產生內存碎片,不過用起來較方便。
使用堆時,頻繁的用new/delete必會形成內存空間的不連續,致使形成大量碎片的產生,下降程序效率。而棧則不會有這個問題,由於棧是先進後出的隊列,它們是如此的一一對應,以致於永遠都不可能有一個內存塊從棧中間彈出。數據結構
咱們在此只是瞭解下基本的數據結構,它們的語言實現、深刻了解請參考其餘資料。函數
①定義:棧是限制插入和刪除只能在一個位置上進行的線性表。該位置即爲表的末端也叫棧頂(top)棧又叫做「先進後出表」。
②基本操做:棧的基本操做包括:push(進棧)和pop(出棧)。
③棧的實現:棧是一個線性表,因此棧能夠用數組或者鏈表來實現(即棧能夠由邏輯上連續的ADT實現)。在java中大部分棧的實現是使用ArrayList或者LinkedList。
④性能:
索引:O(n)
查找:O(n)
插入:O(1)
刪除:O(1)
①定義: 堆是一種特別的樹狀數據結構。堆要知足如下特徵:「給定堆中任意節點P和C,若P是C的母節點,那麼P的值會小於等於(或大於等於)C的值」。
邏輯定義
堆是徹底二叉樹,徹底二叉樹不必定是堆
②堆的實現: 經過構造二叉堆(binary heap),實爲二叉樹的一種;因爲其應用的廣泛性,當不加限定時,均指該數據結構的這種實現。這種數據結構具備如下性質:
①任意節點小於(或大於)它的全部後裔,最小元(或最大元)在堆的根上(堆序性)。
②堆老是一棵徹底樹。即除了最底層,其餘層的節點都被元素填滿,且最底層儘量地從左到右填入。
③將根節點最大的堆叫作最大堆或大根堆,根節點最小的堆叫作最小堆或小根堆。常見的堆有二叉堆、斐波那契堆等。
最大堆:若母節點的值恆小於等於子節點的值,此堆稱爲最小堆(min heap)。
最小堆:若母節點的值恆大於等於子節點的值,此堆稱爲最大堆(max heap)。
在堆中最頂端的那一個節點,稱做根節點(root node),根節點自己沒有母節點(parent node)。
③二叉堆的性能:
索引:O(log(n))
查找:O(log(n))
插入:O(log(n))
刪除:O(log(n))
刪除最大值/最小值:O(1)
④應用:堆排序、優先隊列。