對於初學者來說,記憶體是個神秘的空間。程式的絕大部分錯誤,也是在於記憶體的使用不當造成的,而且這些錯誤有些都是隱藏很深的。所以,如何掌握記憶體的使用,通曉系統對記憶體的管理手段,將是軟體成功的乙個非常關鍵的因素。
首先我們要了解記憶體的分配方式。一般來說,記憶體的分配方式有三種:
1.從靜態儲存區域分配。內存在程式編譯的時候就已經分配好,這塊內存在程式的整個執行期間都存在。例如全域性變數,static變數。
2.在棧上建立。在執行函式時,函式內區域性變數的儲存單元都可以在棧上建立,函式執行結束時這些儲存單元自動被釋放。棧記憶體分配運算內置於處理器的指令集中,效率很高,但是分配的記憶體容量有限。
3.從堆上分配,亦稱動態記憶體分配。程式在執行的時候用malloc或new申請任意多少的記憶體,程式設計師自己負責在何時用free或delete釋放記憶體。動態記憶體的生存期由我們決定,使用非常靈活,但問題也最多。
以上三種分配方式,我們要注意記憶體生命期的問題:
1.靜態分配的區域的生命期是整個軟體執行期,就是說從軟體執行開始到軟體終止退出。只有軟體終止執行後,這塊記憶體才會被系統**2.在
棧中分配的空間的生命期與這個變數所在的函式和類相關。如果是函式中定義的區域性變數,那麼它的生命期就是函式被呼叫時,如果函式執行結束,那麼這塊記憶體就會被**。如果是類中的成員變數,則它的生命期與類例項的生命期相同
3.在堆上分配的記憶體,生命期是從呼叫new或者malloc開始,到呼叫delete或者free結束。如果不掉用delete或者free。則這塊空間必須到軟體執行結束後才能被系統**。
下面我們再看看,在使用記憶體的過程中,我們經常發生一些什麼樣的錯誤。以及我們應該採取哪些對策。
發生記憶體錯誤是件非常麻煩的事情。編譯器不能自動發現這些錯誤,通常是在程式執行時才能捕捉到。而這些錯誤大多沒有明顯的症狀,時隱時現,增加了改錯的難度。有時使用者怒氣沖沖地把你找來,程式卻沒有發生任何問題,你一走,錯誤又發作了。
常見的記憶體錯誤及其對策如下:
1記憶體分配未成功,卻使用了它。
程式設計新手常犯這種錯誤,因為他們沒有意識到記憶體分配會不成功。常用解決辦法是,在使用記憶體之前檢查指標是否為null。如果指標p是函式的引數,那麼在函式的入口處用assert(p!=null)進行檢查。如果是用malloc或new來申請記憶體,應該用if(p==null) 或if(p!=null)進行防錯處理。
2記憶體分配雖然成功,但是尚未初始化就引用它。
犯這種錯誤主要有兩個起因:一是沒有初始化的觀念;二是誤以為記憶體的預設初值全為零,導致引用初值錯誤(例如陣列)。
記憶體的預設初值究竟是什麼並沒有統一的標準,儘管有些時候為零值,我們寧可信其無不可信其有。所以無論用何種方式建立陣列,都別忘了賦初值,即便是賦零值也不可省略,不要嫌麻煩。
3記憶體分配成功並且已經初始化,但操作越過了記憶體的邊界。
例如在使用陣列時經常發生下標「多1」或者「少1」的操作。特別是在for迴圈語句中,迴圈次數很容易搞錯,導致陣列操作越界。
4忘記了釋放記憶體,造成記憶體洩露。
動態記憶體的申請與釋放必須配對,程式中malloc與free的使用次數一定要相同,否則肯定有錯誤(new/delete同理)。
5釋放了記憶體卻繼續使用它。
有三種情況:
(1)程式中的物件呼叫關係過於複雜,實在難以搞清楚某個物件究竟是否已經釋放了記憶體,此時應該重新設計資料結構,從根本上解決物件管理的混亂局面。
(2)函式的return語句寫錯了,注意不要返回指向「棧記憶體」的「指標」或者「引用」,因為該內存在函式體結束時被自動銷毀。
(3)使用free或delete釋放了記憶體後,沒有將指標設定為null。導致產生「野指標」。
綜上所述,我們應該注意:
1.用malloc或new申請記憶體之後,應該立即檢查指標值是否為null。防止使用指標值為null的記憶體。
2.不要忘記為陣列和動態記憶體賦初值。防止將未被初始化的記憶體作為右值使用。
3.避免陣列或指標的下標越界,特別要當心發生「多1」或者「少1」操作。
4.動態記憶體的申請與釋放必須配對,防止記憶體洩漏。
5.用free或delete釋放了記憶體之後,立即將指標設定為null,防止產生「野指標」。
下面舉幾個經典的錯誤例子,大家不要犯同樣的錯誤:
1.返回棧記憶體指標
char *getstring(void)
char* pget = getstring();
這段程式編譯時沒有錯誤,執行也沒有錯誤,但是你卻無法使得返回的pget指標指向的資料是你想要的「hello world」,因為指標p的生命期是函式getstring內,執行完函式getstring後,p分配的棧空間馬上被系統**了。雖然pget指向了p當初分配的記憶體位址,但是那塊位址已經沒有內容了。
2.這是乙個出現頻率非常高的錯誤
char* pchar = new char;
……
int a ;
pchar = &a;
……
delete pchar;
當然這是乙個例子,具體的程式各有不同。
這段程式有兩個問題。一是pchar = &a;將導致pchar原先分配的空間無法再被獲取,就象我們的丟失了朋友的**號碼一樣,無法再聯絡這個朋友了。這就造成了記憶體洩漏。如果記憶體 洩漏多了,可能導致系統的崩潰,因為可用的資源將越來越少,直到枯竭為止。第二個問題是delete pchar將導致異常發生,因為這時的pchar已經不是指向動態分配的記憶體了,而是指向了a分配的棧空間,而棧空間是不能使用delete來**的,因 此將導致記憶體異常。
記憶體是財富,正確使用財富是關鍵,為人如此,程式設計也如此。
[ 收藏到我的網摘] [
特別推薦:
狂想2046 發表於2004-07-07 13:12:00 ip: 61.163.245.*
呵呵,好文,頂之…………
zhoujiamurong 發表於2004-07-07 14:11:00 ip: 218.19.117.*
清楚 hongyang1980 發表於2004-09-23 09:52:00 ip: 218.3.94.*
good
追風的葉 發表於2005-05-17 11:34:00 ip: 221.6.29.*
上面寫的是摘錄林銳的高質量程式設計指南,希望在引用別人的文章內容時,請註明摘錄的出處,表示對作者的尊敬.
suifengpiao 發表於2006-09-25 13:40:00 ip: 58.240.178.*
1. 返回棧記憶體指標
char *getstring(void)
char* pget = getstring();
這段程式編譯時沒有錯誤,執行也沒有錯誤,但是你卻無法使得返回的pget指標指向的資料是你想要的「hello world」,因為指標p的生命期是函式getstring內,執行完函式getstring後,p分配的棧空間馬上被系統**了。雖然pget指向了p 當初分配的記憶體位址,但是那塊位址已經沒有內容了
//*****==
上面錯的很離譜啊
char *p = "hello world";
這裡是區域性常量,
char* pget = getstring();這樣是可以輸出hello world的。
作者所指的應該是下面這種情況:
1. 返回棧記憶體指標
char *getstring(void)
char* pget = getstring();
正確使用記憶體
對於初學者來說,記憶體是個神秘的空間。程式的絕大部分錯誤,也是在於記憶體的使用不當造成的,而且這些錯誤有些都是隱藏很深的。所以,如何掌握記憶體的使用,通曉系統對記憶體的管理手段,將是軟體成功的乙個非常關鍵的因素。首先我們要了解記憶體的分配方式。一般來說,記憶體的分配方式有三種 1 從靜態儲存區域分配...
正確使用記憶體
對於初學者來說,記憶體是個神秘的空間。程式的絕大部分錯誤,也是在於記憶體的使用不當造成的,而且這些錯誤有些都是隱藏很深的。所以,如何掌握記憶體的使用,通曉系統對記憶體的管理手段,將是軟體成功的乙個非常關鍵的因素。首先我們要了解記憶體的分配方式。一般來說,記憶體的分配方式有三種 1 從靜態儲存區域分配...
正確使用記憶體
對於初學者來說,記憶體是個神秘的空間。程式的絕大部分錯誤,也是在於記憶體的使用不當造成的,而且這些錯誤有些都是隱藏很深的。所以,如何掌握記憶體的使用,通曉系統對記憶體的管理手段,將是軟體成功的乙個非常關鍵的因素。首先我們要了解記憶體的分配方式。一般來說,記憶體的分配方式有三種 1 從靜態儲存區域分配...