內存、堆棧和各種變量的理解

        其實早就想梳理下這些知識點,一直礙於沒有合適的時間,沒有合適的心情(那是一種feel,😂),今天大致梳理一下這些知識點,記錄一下。

1、內存

       有時候大家聽到內存這個詞也許會根flash存儲混淆,其實這個也很正常,在我還沒有接觸計算機和技術相關的事情的時候,我也不清楚,大家經常說自己手機內存不夠了,16G,32G不夠用等等之類的,其實正確的叫法應該是flash存儲不夠用,而不是叫做內存。內存是什麼,內存是cpu加載程序運行的地方,我們系統運行起來以後基本所有的操作都在內存中執行,此時我們都可以把我們的硬盤拔掉(SSD或者機械硬盤,SSD本質上就是flash的存儲,只是它集結了很多顆flash然後組合在一起的大存儲而已,機械硬盤和SSD可以看成是一個東西,存儲介質)。拔掉硬盤後,我們的系統還是可以照常在內存中運行,就像我們的筆記本電腦的內存條,終端設備也有內存條,只是大家都不關心它而已,只知道"內存"32G不夠用了,而忽略了運行的內存的存在。這個只是普及一下內存和存儲的區別,我現在要說的這個內存重點不是在這兒,我想記錄的重點是內存中都存放些什麼,他的存儲分配是什麼樣子的。

     作爲一個開發者,我們在開發程序的時候,會關心我們的程序在內存中的存放形式和結構,下面將介紹下一個運行的程序在內存中是有哪些分佈:內存的分佈主要包括棧、堆,全局存儲區(靜態存儲區),常量區金額程序代碼區。

a、堆(heap) :堆的空間一般由程序員自己分配和釋放, 若程序員不釋放,程序結束時可能由OS回收 。malloc和new等操作實際上就是在堆中申請內存,對象使用完後要手動釋放,否則只能等待程序結束時由系統回收,這時將會產生內存泄漏。

b、棧(stack):棧是由編譯器自動分配釋放,例如函數的參數值,局部變量的值等都存放在棧空間裏面,棧的空間很小,一般在1MB左右。

c、全局區(靜態區)(static):全局區存放着全局變量和靜態變量,初始化過的全局變量和靜態變量在同一塊區域,未初始化的全局變量和靜態變量存放在一塊相鄰的區域,此區域由系統在程序結束後釋放,不需要開發者手動申請和釋放。

d、常量區:像宏定義的常量字符串、整數等存放在此區域,在程序結束後由系統釋放。

e、程序代碼區:存放函數體的二進制代碼,就是代碼運行時的相關數據,包括一些靜態動態庫,主函數代碼等。

2、對比一下堆棧的不同點

a、分配方式不同,棧是由系統自己分配,而堆則是開發者通過malloc和new方式自己分配,而且使用完後必須自己釋放,否則將會出現內存泄漏。

b、分配效率不同,棧是機器系統提供的數據結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執 行,所以說棧操作是CPU直接操作的內存寄存器,直接這就決定了棧的效率比較高;而像malloc和new這種申請堆的方式是C++庫提供的方法,它的機制是很複雜的,類似於C++自己封了一層庫去處理管理和控制內存寄存器,所以這樣肯定會存在時差,效率肯定沒有棧那麼高。

c、空間不同,棧的空間一般很小在VC6下面,默認的棧空間是1MB,可能在有些嵌入式設備下棧的空間更小,而堆就不同了,它可以分配更大的空間資源,只要內存足夠大,按理論來說想分多大就分多大

d、碎片問題:

對於棧來說,不存在碎片問題,因爲是直接操作的內存寄存器,只要棧的剩餘空間大於所申請空間,系統將爲程序提供內存,否則將報異常提示棧溢出

而對於堆來說卻不一樣,因爲堆的大小是不確定的,是開發者自己申請釋放的。先大致說下內存怎麼申請的:假如說你要去申請4K的內存,然後C++的庫就會按照你所需要的內存大小去尋找4K大小的空閒內存,慢慢的後面就會有更多的內存申請,比如說64K,128K等申請,期間也伴隨着之前申請的內存被釋放,此時你的內存區域可能就存在這種現象,就是一段內存從左到右斷斷續續的有空閒內存,有在使用的內存,這就是大家常提及的內存碎片化。內存碎片化後就會存在一種不好的現象,就是假如說你要申請一段稍大一點的內存,但是在內存中找不到這樣剛好大小的連續的內存區域,那麼此時C++庫的做法就是會挑幾個空閒的內存拼接起來,形成一個你申請的內存空間,大家都知道如果這樣申請的內存訪問起來肯定沒有連續的訪問起來效率高,這樣性能就會存在問題,這也是內存碎片化後導致的後果。

e、生長方向不同,生長方向不同就是說在堆棧申請的先後順序,內存地址是逐漸變大還是變小來說的。棧是向下生長的,就是說你在函數體內部先後定義了兩個局部變量(局部變量的內存空間的申請是屬於棧空間管理),當你查看它地址的時候你會發現你先定義的變量的內存地址要比你後定義的內存地址要大,對於數組局部變量int data[10],你會發現data[1]要比data[0]的地址小;而堆是向上生長的,就是說你先申請的變量的地址比後申請的變量地址要小,對於malloc申請的內部地址你會發現是從小到大的,和棧的數組地址生長方向相反。

3、變量

在前面也提到過變量,變量分很多種,靜態變量,全局變量,局部變量等等。變量有以下幾個特性:變量生存週期,變量作用域,變量分配方式。

變量生存週期:生存週期就是指這個變量什麼時候產生,什麼時候消失,用通俗的話說就是這個變量的從出生到死亡經理的週期。

變量作用域:作用域的意思就是這個變量在什麼地方起作用,作用於於誰,例如局部變量指作用於該函數內部,函數調用結束後,變量生命週期也結束,作用域也不再存在。

變量分配方式:分配方式指的是這個變量由誰來主動分配,其實前面也提到過,例如像局部變量,靜態變量等是由編譯器或者說是系統給他分配,開發人員不需要關注,但是像堆這種申請的變量,就是開發者給它分配,而且如果不需要或者不用了開發者還要將其釋放。

下面大致說說幾種常用的變量的理解:

a、全局變量:該變量具有全局作用域。全局變量只需在一個源文件中定義,就可以作用於所有的源文件。當然,其他不包含全局變量定義的源文件需要用extern 關鍵字再次聲明這個全局變量。

b、局部變量:該變量也只有局部作用域,它只作用於該函數體內,它在程序運行期間不是一直存在,而是隻在函數執行期間存在,函數的一次調用執行結束後,變量被撤銷,其所佔用的內存也被收回。

c、靜態局部變量:該變量具有局部作用域,它只被初始化一次,自從第一次被初始化直到程序運行結束都一直存在,它和全局變量的區別在於全局變量對所有的函數都是可見的,而靜態局部變量只對定義自己的函數體始終可見(有種站着茅坑有時候拉屎,有時候不拉的感覺,但是它就要一直佔着位置)。

d、靜態全局變量:該變量也具有全局作用域,它與全局變量的區別在於如果程序包含多個文件的話,它作用於定義它的文件裏,不能作用到其它文件裏,即被static關鍵字修飾過的變量具有文件作用域。這樣即使兩個不同的源文件都定義了相同名字的靜態全局變量,它們也是不同的變量(這句感覺很重要,這個是我當時理解的重點)

小結

從分配內存空間看:全局變量,靜態局部變量,靜態全局變量都在靜態存儲區分配空間,而局部變量在棧裏分配空間。從以上分析可以看出, 把局部變量改變爲靜態局部變量後是改變了它的存儲方式即改變了它的生存期。把全局變量改變爲靜態全局變量後是改變了它的作用域,限制了它的使用範圍。因此static 這個說明符在不同的地方所起的作用是不同的。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章