2008-08-11 57 views
16

だから、私はいくつかの助けが必要です。私はC++でプロジェクトを進めています。しかし、私は何とかヒープを破損してしまったと思う。これは私がクラスにstd::stringを追加したという事実に基づいており、それを別のstd::stringから値代入されています。スタックダンプで自分のシステム上メモリ管理、ヒープ破損、およびC++

std::string hello = "Hello, world.\n"; 
/* exampleString = "Hello, world.\n" would work fine. */ 
exampleString = hello; 

クラッシュを。だから基本的には私はを停止し、すべての私のコードとメモリ管理のものを通過し、私が犯した場所を見つける必要があります。コードベースはまだ小さく(約1000行)、これは簡単に実行できます。

それでも、私はもののこの種の私の頭の上ので、私は、私はそこにそれを投げるだろうと思っていました。私は、Linuxシステム上だとvalgrindの周りつついていると、完全に私がやっているかわからないが、それはstd::stringのデストラクタが無効無料だったと報告しました。私は、Googleの検索から「ヒープ腐敗」という言葉を得ることを認めなければならない。この種のものに関する汎用的な記事も高く評価されます。

rm -rf ProjectDir前では、C#で再び実行しますD)

EDIT: 私はそれが明らかにされていませんが、私はのために求めていることの方法は、メモリの問題のこれらの並べ替えの診断助言しています。私はstd :: stringのものが正しいことを知っているので、私が行ったこと(またはバグですが、Selectで問題がありません)です。私は書いたコードをチェックすることができたと確信しています。あなたは非常に賢い人がすぐに問題を見るでしょうが、私はこの種のコード分析を私の 'ツールボックス'に追加したいと思います。

答えて

21

これらはおそらく問題を解決するための比較的安価なメカニズムです:

  1. は私のheap corruption question目を離さない - 彼らは振るように私は答えを更新しています。最初はnew[]delete[]のバランスをとっていましたが、すでにそれを行っています。
  2. valgrindをもう一度やりましょう。それは優れたツールであり、私はそれがWindows上で利用可能であったことを願っています。私はあなたのプログラムを約半分だけ遅くするだけです。これはWindowsの同等物に比べてかなり良いです。
  3. Google Performance Toolsをmalloc/newの代わりに使うことを考えてみましょう。
  4. オブジェクトファイルをすべて消去してから始めましたか?おそらくあなたの作成ファイルは... "最適ではない"
  5. あなたはコード内に十分な数字を入力していません。それを見ていないことをどのように知っていますか? flossingのように、誰もコードのなかでassert()を十分に使います。オブジェクトの検証関数を追加し、メソッドの開始と終了時にその関数を呼び出します。
  6. あなたはcompiling -wallですか?もしそうでなければ、そうする。
  7. PC-Lintのような糸くずツールを見つけてください。あなたのような小さなアプリはPC-lint demoページに収まるかもしれません。つまり、あなたのための購入を意味しません!
  8. ポインタを削除した後で、ポインタをNULLにしていないかどうかを確認してください。誰もぶら下がっているポインタを好まない。宣言されているが割り当てられていないポインタと同じ操作。
  9. アレイの使用を中止してください。代わりにvectorを使用してください。
  10. 生ポインタを使用しないでください。 smart pointerを使用してください。 auto_ptrを使用しないでください!そのことは...驚くべきことです。そのセマンティクスは非常に奇妙です。代わりにBoost smart pointers、またはthe Loki libraryのいずれかを選択します。
+2

+1、良いリスト!しかし、私は#8を争うだろう - 悪いアクセスを防ぐのは間違いだが、それは実際に私の経験では貧弱なロジックやオブジェクトライフタイム管理を隠すコードの臭いだ。 – Roddy

+0

最近、C++には標準で独自のスマートポインタがあるライブラリなので、BoostやLokiの必要はありません。 –

0

は、私の知る限り、あなたのコードが正しいです。 exampleStringは、あなたのようにクラスのスコープを持つstd :: stringであると仮定すると、そのように初期化/割り当てできるはずです。おそらく他にもいくつかの問題がありますか?たぶん、実際のコードのスニペットが文脈に入れるのに役立つでしょう。

質問:新で作成した文字列オブジェクトへのポインタをexampleStringていますか?

1

これは、ヒープの破損かもしれないが、それは腐敗を積み重ねることが同じように可能性があります。ジムは正しい。私たちは本当にもう少し文脈が必要です。これらの2つのソースの行は、私たちにはあまり孤立していません。 C/C++の本当の喜びです。

あなたのコードを投稿し、快適であれば、あなたも、サーバー上でのすべてを投げるとリンクを投稿できます。私はあなたがもっと多くのアドバイスを得られると確信しています(そのうちのいくつかは間違いなくあなたの質問に無関係です)。

1

あなたのコードには間違いがありません。より多くの文脈が必要であると言われている。

まだ試していない場合は、gdb(gccデバッガ)をインストールし、-gを指定してプログラムをコンパイルしてください。これは、gdbが使用できるシンボルをデバッグする際にコンパイルされます。 gdbをインストールしたら、それをプログラム(gdb)で実行します。 Thisはgdbを使用するための便利なチートヒットです。

バグを生成している関数のブレークポイントを設定し、exampleStringの値が何であるかを確認します。また、exampleStringに渡すパラメータにも同じ処理を行います。 std :: stringsが有効であるかどうかを少なくとも通知するはずです。

this articleからの回答がポインタについての良いガイドであることがわかりました。

1

このコードは、単に私のプログラムが失敗した場所(スタック上に割り当てられた場所、Jim)の例です。私は実際には何をしているのかを探しているわけではなく、「どうやって間違っていたのかをどうやって診断するのか」を探しています。人に魚やそのすべてを教える。質問を見ても、私はそれを十分に明確にしていません。編集機能の良さに感謝します。 : ')

また、実際にstd :: stringの問題を修正しました。どうやって?それをベクトルに置き換え、コンパイルしてから、文字列を再び置き換えます。それはで一貫してそこにクラッシュしていてもそれはできませんでした。そこには何か厄介なことがあり、私は何がわからない。私はしかし、私は手動でヒープ上にメモリを割り当てる1時間を確認したいんでした:

this->map = new Area*[largestY + 1]; 
for (int i = 0; i < largestY + 1; i++) { 
    this->map[i] = new Area[largestX + 1]; 
} 

し、それを削除:

for (int i = 0; i < largestY + 1; i++) { 
    delete [] this->map[i]; 
} 
delete [] this->map; 

私は前にC++での2次元配列が割り当てられていません。それは動作するようです。

7

問題をデバッグする方法を知りたい場合は、簡単です。まず、死んだ鶏を手に入れてください。次に、start shaking it

真剣にも、これらの種類のバグを一貫して追跡する方法は見つかっていません。潜在的な問題は非常に多いので、単純なチェックリストはありません。しかし、私は以下をお勧めします:

  1. デバッガで快適になるようにしてください。
  2. デバッガを起動して、怪しいものが見つかるかどうかを確認します。特に、exampleString = hello;行中に何が起こっているかを確認してください。
  3. exampleString = hello;行で実際にクラッシュしていることを確認し、囲みブロックを終了すると(デストラクタを起動させる原因となる可能性がある)チェックしないでください。
  4. あなたが行っている可能性のあるポインタマジックを確認してください。ポインタの算術演算、キャストなど
  5. 割り当てと割り当て解除がすべて一致していることを確認してください(二重割り振り解除なし)。
  6. スタック上のオブジェクトへの参照やポインタが返されていないことを確認してください。

他にも試してみることがたくさんあります。私は他の人たちもアイデアを交わしていると確信しています。

1

また、実際にstd :: stringの問題を修正しました。どうやって?それをベクトルに置き換え、コンパイルしてから、文字列を再び置き換えます。それは一貫してそこに墜落していて、それは固定されていても...できなかった。そこには何か厄介なことがあり、私は何がわからない。

本当にあなたはそれを鶏に振ったようです。 なぜかが動作しているのを知らないと、それでもまだ壊れていて、後で(あなたがさらに複雑になった後に)もう一度あなたを噛み付かせることができます。

3

一部を開始する場所:

をあなたは窓にしている場合、およびVisual C++ 6を使用して(神の誰もが、まだ、これらの日、それを使用しないように、私は願って)それが、STDのimplentationだ:: stringはスレッドセーフではありません、この種のものにつながる可能性があります。私の以前の職場で

Here's an article I found which explains a lot of the common causes of memory leaks and corruption.

我々はこれを支援するコンピュウェアのBoundsCheckerを使用。それは商業的で非常に高価なので、オプションではないかもしれません。

ここではいくつかの使用

http://www.codeguru.com/cpp/misc/misc/memory/article.php/c3745/

http://www.codeproject.com/KB/cpp/MemLeakDetect.aspx

のものとすることができる無料のライブラリができます希望のカップルです。メモリ破損は、すごく魅力的な場所です!

1

実行Purify。

それはマシン・コード・レベルで動作するなど、二重解放、

あなたがメモリをつかうしているとき、あなたは物事を解放しないことにより、メモリリーク、触れてはならない報告します近い魔法のツールです。ソースコードを持っている必要はありません。

Purifyがコードでメモリリークを発見したときに最も楽しい電話会議の1つは、「あなたが関数foo()のメモリを解放していない可能性がありますか?彼らの声に驚きを聞く。

彼らは私たちが神をデバッグしていると思っていましたが、コードを使用する前にPurifyを実行できるように秘密を教えてくれました。 :-)

http://www-306.ibm.com/software/awdtools/purify/unix/

が(それはかなり高価だが、彼らは無料のevalのダウンロード持って)私は(最も極端なすごみの場合を除いて)頻繁に使用するデバッグ手法の

1

一つを分割することですそして征服する。あなたのプログラムが現在いくつかの特定のエラーで失敗した場合は、それを何らかの方法で半分に分けて、それに同じエラーが残っているかどうかを確認してください。明らかに、このトリックはあなたのプログラムを分割する場所を決めることです!

あなたの例は、エラーがどこにあるかを判断するのに十分なコンテキストを示していません。他の誰かがあなたの事例を試してみたら、うまくいくはずです。だからあなたのプログラムでは、あなたが私たちに見せてくれなかった余分なものを取り除き、それがうまくいくかどうか見てみましょう。そうであれば、もう一度もう一度コードを追加してください。それでは、今追加したものがおそらく問題です。

プログラムがマルチスレッドの場合は、おそらく大きな問題があることに注意してください。もしそうでなければ、あなたはこの方法でそれを絞り込むことができるはずです。がんばろう!

1

BoundscheckerやPurifyのようなツール以外にも、このような問題を解決するには、コードを読み込んで作業しているコードに慣れるのが最善の方法です。

メモリ破損はトラブルシューティングが難しい問題の1つで、通常、これらのタイプの問題はデバッガで時間と曜日を費やして "それが削除された後にポインタXが使用されています!

これが役に立ったら、それはあなたが経験を積むにつれてより良くなるものです。

アレイのメモリ割り当ては正しいように見えますが、アレイにアクセスするすべての場所も必ず確認してください。

10

従来の技術、valgrind、浄化などのすべてを逃したバグがありました。クラッシュは大量のメモリと大量の入力データセットでのみ発生しました。

最終的には、デバッガウォッチポイントを使用して追跡しました。ここで手順を説明しようとします:

1)障害の原因を探します。あなたのサンプルコードから、 "exampleString"のメモリが壊れているため、書き込めません。この前提を続けましょう。

2) "exampleString"が問題なく使用または修正された最後の既知の場所にブレークポイントを設定します。

3) 'exampleString'のデータメンバーにウォッチポイントを追加します。私のg ++​​のバージョンでは、文字列は_M_dataplus._M_pに格納されています。このデータメンバーがいつ変更されるかを知りたい。このためGDBの技術がある:私は明らかにグラムでLinuxを使用++、ここでgdbを、私はメモリウォッチポイントは、ほとんどのデバッガで利用可能であると考えています

(gdb) p &exampleString._M_dataplus._M_p 
$3 = (char **) 0xbfccc2d8 
(gdb) watch *$3 
Hardware watchpoint 1: *$3 

。ウォッチポイントがトリガされるまで

4)続行:

Continuing. 
Hardware watchpoint 2: *$3 

Old value = 0xb7ec2604 "" 
New value = 0x804a014 "" 
0xb7e70a1c in std::string::_M_mutate() from /usr/lib/libstdc++.so.6 
(gdb) where 

GDB whereコマンドが変更になったものを示すバックトレースを提供します。これは完全に合法的な変更であり、その場合はそのまま続行するか、またはあなたが幸運であれば、メモリ破損による修正になります。後者の場合は、というコードを実際にはというコードを見直して問題を解決し、うまくいけば解決できるはずです。

私たちのバグの原因は、負のインデックスを持つ配列アクセスでした。インデックスは、配列のサイズを「整数」とするポインタへのキャストの結果でした。このバグはvalgrindらによって見逃された。これらのツールで実行されたときに割り当てられたメモリアドレスは決して "> MAX_INT"であったため、負のインデックスにはなりませんでした。

+0

Linuxの素晴らしい議論!その環境で成長しているミス。自分自身でWinDozeの解決策が必要です...(VS6.0も)...(私のせいではありません!顧客はVS6.0&Customersを常に使います:) –

関連する問題