在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++中,雖然可以自由操作內存,但這種技術就像是一把雙刃劍,用好了鋒利無比,用不好反而會造成一些自己都不能理解的莫名其妙的結果。深入理解內存的分配方式,對於實際編程是大有助益的。