天天看點

深入思考全局靜态存儲區、堆區和棧區

在C++中,記憶體可分為系統資料區,自由存儲區,文本區,const資料區,全局靜态區,堆區和棧區。其中,系統資料區存放的是系統資料,我們是不能自由通路的,有時候windows系統會突然彈出一個消息框,内容是“記憶體不能為read”就是錯誤通路系統資料區的結果;自由存儲區用來存放由C延伸而來的malloc()函數所配置設定的資料;文本區存放着我們的函數代碼,我們調用函數時的底層行為就類似于先去操作一個指針,而這個指針就指向函數指令所在的位址,也就是在文本區中;const資料區,顧名思義,就是存放不可修改的資料的記憶體區域,我們定義的const變量都存放在這裡。最後,我們來看全局靜态存儲區、堆區和棧區。

       先來看全局靜态存儲區,在程式中,由static标号定義的資料都存放在全局靜态存儲區中,不論是在main()函數之外的定義的全局變量,還是在子函數中定義的局部變量,隻要在定義之前有static标号,定義之後就會始終存在于全局靜态存儲區中。當然,在main()函數之外定義的全局靜态變量在任何地方都可以通路,而在子函數中定義的局部靜态變量隻有在定義該變量的子產品中可見。但是也存在這樣一種現象:如前邊所述,即使在子函數中定義的局部靜态變量,其存在形式也是靜态的,也就是說,隻要在變量定義的語句執行之後,即使在變量不可見的地方,隻要對該變量所在的位址取位址解析操作,也是可以獲得該變量的值的。比如我們在函數fun()中定義了一個static int a=100;假設該變量的位址是0x0042AD54,我們在main()函數中調用fun()之後,如果對0x0042AD54取位址解析,也是可以得到100的:int* p=(int*)0x0042ad54; int b=*p;這裡b被指派100。由此,我們可以看到,凡是有static定義的變量的生命周期就是整個程式的生命周期,直到程式退出,靜态變量所占據的記憶體才會被釋放。

        堆存儲區的行為類似于靜态存儲區,當我們在堆上配置設定記憶體之後,如果不進行手動的釋放,其記憶體是不會自動釋放掉的。但是在JAVA中,有一種叫做垃圾清理的機制可以自動清理堆記憶體,但是在C++中沒有這樣的機制。也就是說,在C++中,如果我們配置設定了堆記憶體,就必須手動釋放它。否則如果我們不停的配置設定堆記憶體,但是不對其進行釋放,當對記憶體被耗盡是就會造成程式崩潰。

一般地,用new配置設定的變量是存放于堆記憶體中的,但是傳回的指針變量是存放在棧中的。當我們在一個子函數中new了一個變量,但是在函數傳回時既沒有儲存new傳回的指針,也沒有delete時,就會造成記憶體洩露。如果我們寫的是伺服器程式,不斷地記憶體洩露所造成的最終結果就是伺服器當機。但是在windows、linux以及其他一些成熟的系統中,都有類似于記憶體保護的機制。系統會給使用者程式配置設定一定的運作所需的記憶體,同是也會給系統自身的運作保留一部分記憶體,這部分記憶體是使用者程式所不能通路的。如果我們編寫的程式存在記憶體洩露,當耗盡系統給應用程式配置設定的記憶體之後,程式就會停止運作,而不會造成系統的司機。

       至于棧記憶體,也是我們在寫程式中用到的最多的情況。程式中定義的每一個臨時對象,new所傳回的指針,以及遞歸函數中變量都是存放在棧中的。棧記憶體是可以自動釋放的,當我們在某個子產品中定義了一個對象,在該子產品結束時,變量所占據的記憶體就會被系統回收,在定義新的變量時,新的變量就有可能存放在原變量所在的位址上,但是在系統回收棧記憶體的時候,是不會清空所釋放的棧記憶體中的資料的,隻是将棧頂重新調整,并在新資料的到來時将其配置設定到棧頂。

       在C++中,雖然可以自由操作記憶體,但這種技術就像是一把雙刃劍,用好了鋒利無比,用不好反而會造成一些自己都不能了解的莫名其妙的結果。深入了解記憶體的配置設定方式,對于實際程式設計是大有助益的。

繼續閱讀