天天看點

C++靜态存儲區、棧區和堆區的差別

學習c++如果不了解記憶體配置設定是一件非常可悲的事情。而且,可以這樣講,一個C++程式員無法掌握記憶體、無法了解記憶體,是不能夠成為一個合格的C++程式員的。

    一、記憶體基本構成

    可程式設計記憶體在基本上分為這樣的幾大部分:靜态存儲區、堆區和棧區。他們的功能不同,對他們使用方式也就不同。

    靜态存儲區:記憶體在程式編譯的時候就已經配置設定好,這塊記憶體在程式的整個運作期間都存在。它主要存放靜态資料、全局資料和常量。

    棧區:在執行函數時,函數内局部變量的存儲單元都可以在棧上建立,函數執行結束時這些存儲單元自動被釋放。棧記憶體配置設定運算内置于處理器的指令集中,效率很高,但是配置設定的記憶體容量有限。

    堆區:亦稱動态記憶體配置設定。程式在運作的時候用malloc或new申請任意大小的記憶體,程式員自己負責在适當的時候用free或delete釋放記憶體。動态記憶體的生存期可以由我們決定,如果我們不釋放記憶體,程式将在最後才釋放掉動态記憶體。 但是,良好的程式設計習慣是:如果某動态記憶體不再使用,需要将其釋放掉,否則,我們認為發生了記憶體洩漏現象。

    二、三者之間的差別

    我們通過代碼段來看看對這樣的三部分記憶體需要怎樣的操作和不同,以及應該注意怎樣的地方。

    例一:靜态存儲區與棧區

    char* p = “Hello World1”;

    char a[] = “Hello World2”;

    p[2] =‘A’;

    a[2] =‘A’;

    char* p1 = “Hello World1;”

    C++ 靜态存儲區 棧 堆的差別[網摘] - chen_hans - Seа" style="FILTER: ; WIDTH: 300px; HEIGHT: 249px" height=249 alt="" hspace=0 src="http://c.chinaitlab.com/UploadFiles_4204/200909/20090924101330348.jpg" width=300 border=0>

    這個程式是有錯誤的,錯誤發生在p[2] = ‘A’這行代碼處,為什麼呢,是變量p和變量數組a都存在于棧區的(任何臨時變量都是處于棧區的,包括在main()函數中定義的變量)。但是,資料“Hello World1”和資料“Hello World2”是存儲于不同的區域的。

    因為資料“Hello World2”存在于數組中,是以,此資料存儲于棧區,對它修改是沒有任何問題的。因為指針變量p僅僅能夠存儲某個存儲空間的位址,資料“Hello World1”為字元串常量,是以存儲在靜态存儲區。雖然通過p[2]可以通路到靜态存儲區中的第三個資料單元,即字元‘l’所在的存儲的單元。但是因為資料“Hello World1”為字元串常量,不可以改變,是以在程式運作時,會報告記憶體錯誤。并且,如果此時對p和p1輸出的時候會發現p和p1裡面儲存的位址是完全相同的。換句話說,在資料區隻保留一份相同的資料(見圖1-1)。

    例二:棧區與堆區

    char* f1()

    {

    char* p = NULL;

    char a;

    p =

    return p;

    }

    char* f2()

    char* p = NULL:

    p =(char*)new char[4];

    這兩個函數都是将某個存儲空間的位址傳回,二者有何差別呢?f1()函數雖然傳回的是一個存儲空間,但是此空間為臨時空間。也就是說,此空間隻有短暫的生命周期,它的生命周期在函數f1()調用結束時,也就失去了它的生命價值,即:此空間被釋放掉。是以,當調用f1()函數時,如果程式中有下面的語句:

    char* p;

    p = f1();

    *p =‘a’;

    此時,編譯并不會報告錯誤,但是在程式運作時,會發生異常錯誤。因為,你對不應該操作的記憶體(即,已經釋放掉的存儲空間)進行了操作。但是,相比之下,f2()函數不會有任何問題。因為,new這個指令是在堆中申請存儲空間,一旦申請成功,除非你将其delete或者程式終結,這塊記憶體将一直存在。也可以這樣了解,堆記憶體是共享單元,能夠被多個函數共同通路。如果你需要有多個資料傳回卻苦無辦法,堆記憶體将是一個很好的選擇。但是一定要避免下面的事情發生:

    void f()

    …

    char * p;

    p =(char*)new char[100];

    這個程式做了一件很無意義并且會帶來很大危害的事情。因為,雖然申請了堆記憶體,p儲存了堆記憶體的首位址。但是,此變量是臨時變量,當函數調用結束時p變量消失。也就是說,再也沒有變量存儲這塊堆記憶體的首位址,我們将永遠無法再使用那塊堆記憶體了。但是,這塊堆記憶體卻一直辨別被你所使用(因為沒有到程式結束,你也沒有将其delete,是以這塊堆記憶體一直被辨別擁有者是目前您的程式),進而其他程序或程式無法使用。我們将這種不道德的“流氓行為”(我們不用,卻也不讓别人使用)稱為記憶體洩漏。這是我們C++程式員的大忌!!請大家一定要避免這件事情的發生。

    總之,對于堆區、棧區和靜态存儲區它們之間最大的不同在于,棧的生命周期很短暫。但是堆區和靜态存儲區的生命周期相當于與程式的生命同時存在(如果您不在程式運作中間将堆記憶體delete的話),我們将這種變量或資料成為全局變量或資料。但是,對于堆區的記憶體空間使用更加靈活,因為它允許你在不需要它的時候,随時将它釋放掉,而靜态存儲區将一直存在于程式的整個生命周期中。