
記憶體管理是計算機學習程式設計的一個重要知識,也是令大多數程式員比較頭疼的一個知識。由于在目前的嵌入式系統中資源仍然是有限的,是以對記憶體的管理就顯得尤為重要。C語言程式的記憶體接口簡單,記憶體管理靈活,是以是初學者容易出錯的知識,下面就讓我們分三篇文章進行講解,這篇文章主要講解的是記憶體管理中的堆與棧。
一、堆與棧特點
棧:由系統自動配置設定的釋放,用來存放函數的參數、局部變量的值。有先進先出的特點
堆:由程式員配置設定釋放,若程式員為進行釋放會由OS進行回收。
二、什麼是堆?
通過上面的兩句話隻是簡單的概括了一下堆棧的特點,并不能解釋什麼是堆和棧,下面就讓我們詳細的看一下C語言中的堆是如何解釋的。
一個為計算機程式可以分為兩個部分:存放代碼的代碼段和存放變量和資料的資料段,資料段中又增加了全局初始化資料區和未初始化資料區。其中全局初始化資料區包含程式中明确被初始化的全局變量和靜态變量以及常量。
其中堆是一個位于未初始化資料區(BSS)段和棧之間,用來動态配置設定記憶體。這段區域由程式員管理,程式員利用作業系統提供的配置設定和釋放函數可以使用堆區的記憶體,每個程式員會進行掃描未用空間,當一個空間的大小符合申請空間時,就會将此空間傳回給程式員,同時把申請加入到連結清單,作業系統就是通過此連結清單來維護一個堆的使用。
為避免程式員頻繁申請小的堆,我們在申請的時候需要遵守以下幾點:
1、使用者需要自己配置設定記憶體在堆區,便于使用者管理記憶體以及作業系統監控
2、臨時資料放在棧區,生命周期短
3、全局資料和靜态資料在程式中都能被通路,是以單獨儲存管理
4、程式運作是按順序進行的,雖有跳轉,資料需要多次通路,開辟單獨的資料空間友善資料的通路與分分類。
三、什麼是棧?
棧是由編譯器自己配置設定和釋放空間的一個區域,用來存儲函數的參數、局部變量等。當函數被調用時,被調用的函數參數和傳回值被儲存在目前函數棧區之後調用函數再為自身的自動變量和臨時變量在棧區配置設定空間。當函數調用傳回時,在棧區内的參數傳回值、自動變量和臨時變量等會被自動釋放。
函數的調用和棧的使用方式保證了函數内部定義相同名字的變量不會混淆。棧的管理方式為先進先出,學過C語言的應該知道這個特點。
四、執行個體中的堆與棧
在實際程式設計過程中,可以通過兩種形式來實作:數組的形式實作棧,稱為靜态棧;以連結清單的形式來實作棧,稱為動态棧;
相對于棧“先進先出”的特點,堆則為一種經過排序後形成樹形資料結構,常用來實作優先隊列等。由此可見堆是一種特殊的二叉樹。其中節點從左到右填滿,并且最後一個樹葉在最左邊。
由此可見,記憶體配置設定的堆棧與資料結構中所闡述的堆棧有着本質的差別,這一點千萬不要混淆。同樣,在記憶體配置設定中的堆和棧也存在着很大的差別,也不要混淆這兩者的概念。為了加深了解,看下面一段示例代碼:
該示例代碼主要示範了在記憶體配置設定中的堆和棧的差別,其運作結果為:
五、記憶體中的堆與棧
在C語言中,記憶體配置設定的方式一般有以下三種,分别是靜态存儲區域配置設定;在棧上配置設定;從棧上配置設定;
從靜态存儲區配置設定:由編譯器自動配置設定和釋放的,在程式的整個運作期間都存在,直到整個程式運作結束時才被釋放,比如常見的全局變量與 static 變量。
在棧上配置設定:它同樣也是由編譯器自動配置設定和釋放的,即在執行函數時,函數内局部變量的存儲單元都可以在棧上建立,函數執行結束時這些存儲單元将被自動釋放。需要注意的是,棧記憶體配置設定運算内置于處理器的指令集中,它的運作效率一般很高,但是配置設定的記憶體容量有限。
從堆上配置設定:也被稱為動态記憶體配置設定,它是由程式員手動完成申請和釋放的。即程式在運作的時候由程式員使用記憶體配置設定函數(如 malloc 函數)來申請任意多少的記憶體,使用完之後再由程式員自己負責使用記憶體釋放函數(如 free 函數)來釋放記憶體。
也就是說,動态記憶體的整個生存期是由程式員自己決定的,使用非常靈活。需要注意的是,如果在堆上配置設定了記憶體空間,就必須及時釋放它,否則将會導緻運作的程式出現記憶體洩漏等錯誤。
六、堆與棧深度講解
棧和堆的配置設定方式的差别造成對棧記憶體的自動釋放而言,雖然堆上的資料隻要程式員不釋放空間就可以一直通路,但是,如果一旦忘記了釋放堆記憶體,那麼将會造成記憶體洩漏,導緻程式出現緻命的潛在錯誤。
對堆來說,頻繁配置設定和釋放(malloc / free)不同大小的堆空間勢必會造成記憶體空間的不連續,進而造成大量碎片,導緻程式效率降低;而對棧來講,則不會存在這個問題。
棧是機器系統提供的資料結構,計算機會在底層對棧提供支援,而堆則不同,它是由 C/C++ 函數庫提供的,它的機制也相當複雜。很顯然,堆的配置設定效率比棧要低得多。
由于作業系統是用連結清單來存儲空閑記憶體位址(記憶體區域不連續)的,同時連結清單的周遊方向是由低位址向高位址進行的。是以,堆記憶體的申請大小受限于計算機系統中有效的虛拟記憶體。
而棧則不同,它是一塊連續的記憶體區域,其位址的增長方向是向下進行的,向記憶體位址減小的方向增長。由此可見,棧頂的位址和棧的最大容量一般都是由系統預先規定好的,如果申請的空間超過棧的剩餘空間時,将會提示溢出錯誤。由此可見,相對于堆,能夠從棧中獲得的空間相對較小。
七、變量類型
最後介紹一下 C 語言中各類型變量的存儲位置和作用域。
全局變量:從靜态存儲區域配置設定,其作用域是全局作用域,也就是整個程式的生命周期内都可以使用。與此同時,如果程式是由多個源檔案構成的,那麼全局變量隻要在一個檔案中定義,就可以在其他所有的檔案中使用,但必須在其他檔案中通過使用extern關鍵字來聲明該全局變量。
全局靜态變量:從靜态存儲區域配置設定,其生命周期也是與整個程式同在的,從程式開始到結束一直起作用。但是與全局變量不同的是,全局靜态變量作用域隻在定義它的一個源檔案内,其他源檔案不能使用。
局部變量:從棧上配置設定,其作用域隻是在局部函數内,在定義該變量的函數内,隻要出了該函數,該局部變量就不再起作用,該變量的生命周期也隻是和該函數同在。
局部靜态變量:從靜态存儲區域配置設定,其在第一次初始化後就一直存在直到程式結束,該變量的特點是其作用域隻在定義它的函數内可見,出了該函數就不可見了。
往期回顧
01 |ESP8266子產品詳解 |
02 |DA轉換器是什麼?快來一起學習一下吧! |
03 |STM32中斷優先級詳解 |
04 |STM32下載下傳程式新思路--使用序列槽下載下傳程式 |