C++常見內存錯誤及解決方案

轉自 本文作者:Tocy e-mail: [email protected]

https://www.cnblogs.com/tocy/archive/2012/05/19/2508642.html

 

C++中內存錯誤通常屬於運行時錯誤,只有在程序運行時才能發現,編譯器無法自動檢測到內存錯誤。多數情況下是程序邏輯或者參數存在某些錯誤。下面總結一下C++常見的內存錯誤:

1. 內存泄露

內存泄露是指應用程序未釋放動態申請的且不再使用的內存,原因可能是程序員疏忽或者錯誤造成程序異常。

在C/C++中,動態申請的內存是在堆上的。內存管理器也不會自動回收不再使用的內存,也就是說如果忘記釋放動態申請的內存,該內存區域是不允許重用的。如果發送此類的內存泄露,函數每執行一次就丟失一塊內存。長時間運行改程序可能引起系統"內存耗盡"。

這個問題本身沒有很好的解決思路。只能從編程習慣上入手,也就是說動態申請內存與釋放內存必須匹配,亦即new和delete的調用次數必須相同。

 

2. 野指針

未初始化的指針稱爲野指針(另一種說法是指向不可用內存區域的指針,不過筆者認爲前者更合適)。通常對野指針進行寫操作會發生不可預知的錯誤。

通常的避免方法就是在指針定義的時候就初始化,初始爲NULL或者一個有意義的內存地址。對於動態申請的內存地址,在該內存釋放之後,對應指針最好立即賦值爲NULL。並在具體使用指針的時候判斷指針的值是否有效(通常檢測是否爲NULL)。

 

3. 內存越界訪問

內存越界訪問通常發生在數組、字符串或者連續內存的訪問。有兩種情況:

讀越界,即讀了非有效的數據。如果所讀的內存地址是無效的,程序會立即崩潰。如果所讀內存地址是有效的,讀入的時候不會有錯誤,但是讀入的數據是隨機的,可能會產生不可控制的後果。舉個簡單的例子,字符串的輸出,如果沒有結束符,會輸出一堆亂碼也可能輸出正常,也就是說結果是不可控的。

寫越界,亦稱爲緩衝區溢出,通常寫越界會發生錯誤。內存寫越界造成的後果是非常嚴重的。例如訪問數組越界可能會修改訪問數組的循環變量,造成死循環。另一個比較經典的例子就是緩衝區溢出攻擊,試試上就是利用越界修改程序的棧空間,達到控制操作系統或者執行某些特定任務的目的。

這類問題幾乎沒有很有效的解決思路,只能由程序員控制好內存的訪問,小心處理各種內存有關的操作。

 

4. 返回指向臨時變量的指針

最經典的例子是一道面試題中關於字符串指針的返回函數,代碼如下:

char * getString(){char b[] = "Hello, Tocy!"; return b;}

棧裏面的變量都是臨時的,函數執行完成之後,相關的臨時變量和參數都會被清除。這也是程序不允許返回指向這些臨時變量的指針的原因,因爲函數執行結束後這些指針指向的數據是隨機的,給程序造成不可預知的後果。

通常此類錯誤編譯器會給出警告。解決思路很簡單,在任何情況下不要返回函數的局部變量的任何指針,如果需要傳遞參數可以考慮使用返回值、參數或者全局變量(不推薦)。

 

5. 試圖修改常量

在普通變量前面加上const修飾符,只是給編譯器做類型檢查用的。編譯器禁止修改這樣的變量,但這並不是強制的,完全可以用強制類型轉換來處理,一般不會出錯。例如下面代碼:

int func(void){ const int IMAX = 3; int * pi = (int *)(&IMAX); *pi = 4; cout << IMAX << endl;}

筆者在vc6和vs2008下運行該函數,輸出都是3,編譯運行沒有任何錯誤和警告。至於有沒有修改常量的值,有興趣的讀者可以自己看看反彙編的代碼,相信你就會明白是否修改了常量的值(最終結果是修改了,只是編譯器做了某些優化所以輸出依然不變。)。

而對於全局常量和字符串使用強制類型轉來處理在運行時仍然會出錯,這是因爲它們是存放在只讀內存區("rodata"),只讀內存區是不允許修改的。試圖對其修改,會引發內存錯誤。

所以針對這種類型錯誤,筆者建議最好不要修改常量,除非萬不得已。

 

6. 內存未分配成功,但已經使用

通常是由於程序員認爲動態內存分配不會失敗。解決思路很簡單,在使用動態申請的內存時,首先檢測指針是否爲NULL(通常動態內存分配失敗會返回空指針)。

 

7. 內存分配成功,但沒有初始化

犯這種錯誤主要有兩個起因:一是沒有初始化的觀念;二是誤以爲內存的缺省初值全爲零,導致引用初值錯誤(例如數組)。

內存的缺省初值究竟是什麼並沒有統一的標準,儘管有些時候爲零值,我們寧可信其無不可信其有。所以無論用何種方式創建數組,都別忘了賦初值,即便是賦零值也不可省略,不要嫌麻煩。

當然發生這種情況還有另外一個可能就是在動態創建類對象時類構造函數拋出異常(即申請動態內存成功,但是調用構造函數失敗)。

 

另外如果出現下圖

基本可以斷定是內存問題,極有可能是指針異常引起的。當然還有其他原因,需要視具體情況而定。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章