全局靜態(tài)存儲區(qū)、堆區(qū)和棧區(qū)深入剖析
更新時間:2012年11月12日 11:41:16 作者:
在C++中,內存可分為系統(tǒng)數據區(qū),自由存儲區(qū),文本區(qū),const數據區(qū),全局靜態(tài)區(qū),堆區(qū)和棧區(qū)
在C++中,內存可分為系統(tǒng)數據區(qū),自由存儲區(qū),文本區(qū),const數據區(qū),全局靜態(tài)區(qū),堆區(qū)和棧區(qū)。其中,系統(tǒng)數據區(qū)存放的是系統(tǒng)數據,我們是不能自由訪問的,有時候windows系統(tǒng)會突然彈出一個消息框,內容是“內存不能為read”就是錯誤訪問系統(tǒng)數據區(qū)的結果;自由存儲區(qū)用來存放由C延伸而來的malloc()函數所分配的數據;文本區(qū)存放著我們的函數代碼,我們調用函數時的底層行為就類似于先去操作一個指針,而這個指針就指向函數指令所在的地址,也就是在文本區(qū)中;const數據區(qū),顧名思義,就是存放不可修改的數據的內存區(qū)域,我們定義的const變量都存放在這里。最后,我們來看全局靜態(tài)存儲區(qū)、堆區(qū)和棧區(qū)。
先來看全局靜態(tài)存儲區(qū),在程序中,由static標號定義的數據都存放在全局靜態(tài)存儲區(qū)中,不論是在main()函數之外的定義的全局變量,還是在子函數中定義的局部變量,只要在定義之前有static標號,定義之后就會始終存在于全局靜態(tài)存儲區(qū)中。當然,在main()函數之外定義的全局靜態(tài)變量在任何地方都可以訪問,而在子函數中定義的局部靜態(tài)變量只有在定義該變量的模塊中可見。但是也存在這樣一種現象:如前邊所述,即使在子函數中定義的局部靜態(tài)變量,其存在形式也是靜態(tài)的,也就是說,只要在變量定義的語句執(zhí)行之后,即使在變量不可見的地方,只要對該變量所在的地址取地址解析操作,也是可以獲得該變量的值的。比如我們在函數fun()中定義了一個static int a=100;假設該變量的地址是0x0042AD54,我們在main()函數中調用fun()之后,如果對0x0042AD54取地址解析,也是可以得到100的:int* p=(int*)0x0042ad54; int b=*p;這里b被賦值100。由此,我們可以看到,凡是有static定義的變量的生命周期就是整個程序的生命周期,直到程序退出,靜態(tài)變量所占據的內存才會被釋放。
堆存儲區(qū)的行為類似于靜態(tài)存儲區(qū),當我們在堆上分配內存之后,如果不進行手動的釋放,其內存是不會自動釋放掉的。但是在JAVA中,有一種叫做垃圾清理的機制可以自動清理堆內存,但是在C++中沒有這樣的機制。也就是說,在C++中,如果我們分配了堆內存,就必須手動釋放它。否則如果我們不停的分配堆內存,但是不對其進行釋放,當對內存被耗盡是就會造成程序崩潰。
一般地,用new分配的變量是存放于堆內存中的,但是返回的指針變量是存放在棧中的。當我們在一個子函數中new了一個變量,但是在函數返回時既沒有保存new返回的指針,也沒有delete時,就會造成內存泄露。如果我們寫的是服務器程序,不斷地內存泄露所造成的最終結果就是服務器死機。但是在windows、linux以及其他一些成熟的系統(tǒng)中,都有類似于內存保護的機制。系統(tǒng)會給用戶程序分配一定的運行所需的內存,同是也會給系統(tǒng)自身的運行保留一部分內存,這部分內存是用戶程序所不能訪問的。如果我們編寫的程序存在內存泄露,當耗盡系統(tǒng)給應用程序分配的內存之后,程序就會停止運行,而不會造成系統(tǒng)的司機。
至于棧內存,也是我們在寫程序中用到的最多的情況。程序中定義的每一個臨時對象,new所返回的指針,以及遞歸函數中變量都是存放在棧中的。棧內存是可以自動釋放的,當我們在某個模塊中定義了一個對象,在該模塊結束時,變量所占據的內存就會被系統(tǒng)回收,在定義新的變量時,新的變量就有可能存放在原變量所在的地址上,但是在系統(tǒng)回收棧內存的時候,是不會清空所釋放的棧內存中的數據的,只是將棧頂重新調整,并在新數據的到來時將其分配到棧頂。
在C++中,雖然可以自由操作內存,但這種技術就像是一把雙刃劍,用好了鋒利無比,用不好反而會造成一些自己都不能理解的莫名其妙的結果。深入理解內存的分配方式,對于實際編程是大有助益的。
先來看全局靜態(tài)存儲區(qū),在程序中,由static標號定義的數據都存放在全局靜態(tài)存儲區(qū)中,不論是在main()函數之外的定義的全局變量,還是在子函數中定義的局部變量,只要在定義之前有static標號,定義之后就會始終存在于全局靜態(tài)存儲區(qū)中。當然,在main()函數之外定義的全局靜態(tài)變量在任何地方都可以訪問,而在子函數中定義的局部靜態(tài)變量只有在定義該變量的模塊中可見。但是也存在這樣一種現象:如前邊所述,即使在子函數中定義的局部靜態(tài)變量,其存在形式也是靜態(tài)的,也就是說,只要在變量定義的語句執(zhí)行之后,即使在變量不可見的地方,只要對該變量所在的地址取地址解析操作,也是可以獲得該變量的值的。比如我們在函數fun()中定義了一個static int a=100;假設該變量的地址是0x0042AD54,我們在main()函數中調用fun()之后,如果對0x0042AD54取地址解析,也是可以得到100的:int* p=(int*)0x0042ad54; int b=*p;這里b被賦值100。由此,我們可以看到,凡是有static定義的變量的生命周期就是整個程序的生命周期,直到程序退出,靜態(tài)變量所占據的內存才會被釋放。
堆存儲區(qū)的行為類似于靜態(tài)存儲區(qū),當我們在堆上分配內存之后,如果不進行手動的釋放,其內存是不會自動釋放掉的。但是在JAVA中,有一種叫做垃圾清理的機制可以自動清理堆內存,但是在C++中沒有這樣的機制。也就是說,在C++中,如果我們分配了堆內存,就必須手動釋放它。否則如果我們不停的分配堆內存,但是不對其進行釋放,當對內存被耗盡是就會造成程序崩潰。
一般地,用new分配的變量是存放于堆內存中的,但是返回的指針變量是存放在棧中的。當我們在一個子函數中new了一個變量,但是在函數返回時既沒有保存new返回的指針,也沒有delete時,就會造成內存泄露。如果我們寫的是服務器程序,不斷地內存泄露所造成的最終結果就是服務器死機。但是在windows、linux以及其他一些成熟的系統(tǒng)中,都有類似于內存保護的機制。系統(tǒng)會給用戶程序分配一定的運行所需的內存,同是也會給系統(tǒng)自身的運行保留一部分內存,這部分內存是用戶程序所不能訪問的。如果我們編寫的程序存在內存泄露,當耗盡系統(tǒng)給應用程序分配的內存之后,程序就會停止運行,而不會造成系統(tǒng)的司機。
至于棧內存,也是我們在寫程序中用到的最多的情況。程序中定義的每一個臨時對象,new所返回的指針,以及遞歸函數中變量都是存放在棧中的。棧內存是可以自動釋放的,當我們在某個模塊中定義了一個對象,在該模塊結束時,變量所占據的內存就會被系統(tǒng)回收,在定義新的變量時,新的變量就有可能存放在原變量所在的地址上,但是在系統(tǒng)回收棧內存的時候,是不會清空所釋放的棧內存中的數據的,只是將棧頂重新調整,并在新數據的到來時將其分配到棧頂。
在C++中,雖然可以自由操作內存,但這種技術就像是一把雙刃劍,用好了鋒利無比,用不好反而會造成一些自己都不能理解的莫名其妙的結果。深入理解內存的分配方式,對于實際編程是大有助益的。
相關文章
C語言實現將double/float 轉為字符串(帶自定義精度)
這篇文章主要介紹了C語言實現將double/float 轉為字符串(帶自定義精度),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12