2010-08-17 44 views
2

我想知道與C和C++相關的常見內存管理問題。我們如何調試這些錯誤。C++中的內存管理問題

這裏有一些我知道

1)未初始化的變量使用

2)刪除指針兩次

3)書寫陣列出未能解除分配內存邊界

4)的

5)競賽條件

1)malloc傳回一個NULL指針。你需要將這個指針轉換爲任何你想要的。

2)對於字符串,需要爲結尾字符分配一個額外的字節。

3)雙指針。

4)(刪除)和malloc()和(自由和新的)不一起去

5)看到什麼實際的函數返回(返回代碼)失敗並釋放內存,如果它失敗。 6)檢查大小分配存儲器的malloc(FUNC 1)

7)檢查如何ü通過雙足尖** PTR起作用

8)檢查數據大小行爲未定義函數調用

9)內存分配失敗

+6

解決它們?他們是錯誤的,只是不要做導致他們的行爲。 – James 2010-08-17 20:02:05

+3

由於您要求提供一個列表,而不是一個正確的答案,所以這應該是社區wiki。 – Cascabel 2010-08-17 20:03:00

+1

它沒有解決它的調試 – brett 2010-08-17 20:04:17

回答

6

先發制人預防這些錯誤擺在首位:

1)打開警告,錯誤級別,克服了未初始化錯誤。編譯器會經常發出這樣的警告,並通過讓他們訪問錯誤來解決問題。

2)使用Smart pointers。你可以在Boost中找到這樣的好東西。 3)使用vectors或其他STL containers。除非你使用Boost variety之一,否則不要使用數組。

4)再次,使用容器對象或智能指針爲您處理此問題。

5)在任何地方都可以使用不可變的數據結構,並在共享可變對象的修改點周圍放置鎖。

與傳統應用程序

1)同上的處理。

2)使用集成測試來了解應用程序的不同組件如何發揮出來。這應該會發現許多這種錯誤的情況。認真考慮由另一個小組完成正式的同行評審,撰寫與你的裸指接觸的應用程序的不同部分。

3)您可以重載new運算符,以便它在對象前後分配一個額外的字節。這些字節應該填充一些容易識別的值,例如0xDEADBEEF。所有你需要做的就是檢查前後的字節,以便見證你的內存是否被這樣的錯誤破壞。

4)通過多次運行應用程序的各種組件來跟蹤您的內存使用情況。如果你的內存增長,檢查丟失的釋放。

5)祝你好運。對不起,但這是可以在99.9%的時間內工作的事情之一,然後,繁榮!客戶抱怨。

+0

+1:這是一個好的列表 – Arun 2010-08-17 20:21:56

+0

謝謝。我已經處理了由最低出價轉包商構建的第三方「C++」應用程序的公平份額,這些應用程序只返回了裸指針。 – wheaties 2010-08-17 20:30:35

9

使用RAII(資源獲取初始化)。你幾乎不應該在你的代碼中直接使用new和delete。

+0

+1爲理想。但是,有很多API(特別是在嵌入式領域)與RAI混淆。 – 2010-08-17 20:04:47

+0

這是一種預防措施,它不適用於調試。 – Potatoswatter 2010-08-17 21:32:53

+1

搞砸RAII的API通常可以隱藏在隱藏醜陋的類中。但我沒有做嵌入的東西,所以YMMV在那裏。 – 2010-08-20 20:09:54

0

我所知道的最好技術是避免直接執行指針操作和動態分配。在C++中,使用引用參數優先於指針。使用stl對象而不是滾動您自己的列表和容器。使用std::string而不是char *。如果沒有這些,請採取Rob K的建議,並在需要進行分區的地方使用RAII。

對於C來說,有一些類似的事情可以嘗試去做,但是你幾乎註定要失敗。獲得Lint的副本並祈求憐憫。

1

一個你忘記了:

6)解除引用後,它已被釋放。

到目前爲止,大家似乎都在回答「如何預防」,而不是「如何調試」。

假設你正在處理已經存在這些問題的代碼,這裏有一些關於調試的想法。

  • 未初始化的變量使用

    編譯器可以檢測出了很多本。將RAM初始化爲一個已知值有助於調試那些轉義的內容。在我們的嵌入式系統中,我們在離開引導加載程序之前進行內存測試,這會將所有內存設置爲0x5555。這對於調試非常有用:當一個整數== 21845時,我們知道它從未初始化過。

  • 刪除指針兩次

Visual Studio中應在運行時檢測此。如果你懷疑這是在其他系統中發生的事情,你可以通過定製代碼來替換刪除調用像

void delete(void*p){ assert(*(int*)p!=p); _system_delete(p); *(int*)p=p;} 
  • 寫作數組越界調試

的Visual Studio應該在運行時檢測出。在其他系統中,添加自己的哨兵

int headZONE = 0xDEAD; 
int array[whatever]; 
int tailZONE = 0xDEAD; 

//add this line to check for overruns 
//- place it using binary search to zero in on trouble spot 
assert(headZONE==tailZONE&&tailZONE==0xDEAD) 
  • 未能釋放內存

    觀看堆棧增長。記錄創建和銷燬對象之前和之後的空閒堆大小;尋找意想不到的變化。可能在內存函數週圍編寫自己的包裝以跟蹤塊。

  • 競賽條件

    aaargh。確保你有一個準確的時間戳記錄系統。

1
  1. 使用一個很好的編譯器,並設置警告級別設置爲最大
  2. 總結新/ malloc和刪除/自由bookkeep所有的分配/釋放操作
  3. 一個數組的類,它的邊界檢查更換原陣列(或使用std ::矢量)(很難在C做)
  4. 請參閱第2
  5. 這是很難的,存在一些特殊的調試程序,如厄運,專門在這一點,但我不知道他們是怎麼好。
+0

還有std :: array。在C++ 0x。 – Arun 2010-08-17 20:19:18

1

確保您瞭解何時將對象放置在堆上以及何時放在堆棧上。作爲一般規則,如果必要的話,只將對象放在堆上,這樣可以避免很多麻煩。學習STL並使用標準庫提供的容器。

0

我使用的一種常見模式如下。

我把以下三個私人變量中的所有分配器類:

size_t news_; 
    size_t deletes_; 
    size_t in_use_; 

中的分配部構造,三者都被初始化爲0。

上。然後, 每當分配不一個新的,它增加news_,並 每當分配器做一個刪除,它增量deletes_

基於此,我把很多屁股ers在分配器代碼中爲:

assert(news_ - deletes_ == in_use_); 

這對我來說非常好。

加成:我把斷言爲前提和分配器的所有非平凡的方法後置條件。如果斷言blovs,那麼我知道我做錯了什麼。如果斷言沒有發揮作用,那麼我可以做所有的測試,那麼我對我的程序的內存管理正確性有充分的信心。

2

除了已經說過的所有內容之外,還可以使用valgrind或Bounds Checker來檢測程序中的所有錯誤(競賽條件除外)。

1

1)未初始化的變量使用

由編譯器自動檢測(轉警示全和警告視爲錯誤)。

2)刪除一個指針兩次

不要使用原始指針。所有指針都應該在智能指針或管理指針生命週期的某種形式的RAII對象中。

3)寫數組越界

不這樣做的。這是一個合乎邏輯的錯誤。 您可以通過uisng容器和拋出的出界訪問的方法(矢量::在())

4)不釋放內存

不要使用RAW減輕它指針。參見上面的(2)。

5)競爭條件

不要讓它們。按優先級順序分配資源以避免衝突鎖定,然後在存在多個寫入訪問的可能性(或重要時爲讀取訪問)時鎖定對象。

+0

+1。此外,#1,你的意思是「把**警告作爲錯誤**。 – Poni 2010-08-17 21:00:23