20

だから、私はいくつかの助けが必要です。私はC++でプロジェクトに取り組んでいます。しかし、どういうわけかヒープを壊してしまったと思います。std::stringこれは、クラスにを追加し、別のクラスから値を割り当てたという事実に基づいていstd::stringます。

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)

編集:私はそれを明確にしませんでしたが、私が求めているのは、この種のメモリの問題を診断するためのアドバイスです。std :: stringのものが正しいことを知っているので、それは私がやったことです(またはバグですが、Selectに問題はありません)。私が書いたコードをチェックでき、非常に賢い人ならすぐに問題を見つけることができると確信していますが、この種のコード分析を私の「ツールボックス」に追加したいと思います。

4

12 に答える 12

23

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

  1. 私のヒープ破損の質問に目を光らせてください-私は彼らが揺れ動くので答えで更新しています。1つ目はバランスnew[]delete[]ですが、あなたはすでにそれを行っています。
  2. valgrindをもっと試してみてください。これは優れたツールであり、Windowsで利用できることを願っています。私はあなたのプログラムを約半分だけ遅くします、それはWindowsの同等のものと比較してかなり良いです。
  3. GooglePerformanceToolsをmalloc/newの代わりに使用することを検討してください。
  4. すべてのオブジェクトファイルをクリーンアップして最初からやり直しましたか?おそらくあなたのmakeファイルは...「次善」です
  5. assert()コードが十分に機能していません。それを見ずにどうやってそれを知ることができますか?デンタルフロスのように、誰もassert()彼らのコードで十分ではありません。オブジェクトの検証関数を追加し、メソッドの開始時とメソッドの終了時に呼び出します。
  6. -wallをコンパイルしていますか?そうでない場合は、そうしてください。
  7. PC-Lintのようなリントツールを見つけてください。あなたのような小さなアプリがPC-lintデモページに収まるかもしれません。つまり、購入する必要はありません。
  8. ポインタを削除した後、ポインタをNULLにしていることを確認してください。ダングリングポインタが好きな人はいません。宣言されているが割り当てられていないポインターを使用した同じギグ。
  9. アレイの使用を停止します。代わりにベクトルを使用してください。
  10. 生のポインタは使用しないでください。スマートポインタを使用します。使用しないでくださいauto_ptr!それは...驚くべきことです。そのセマンティクスは非常に奇妙です。代わりに、Boostスマートポインターの1つ、またはLokiライブラリから何かを選択してください。
于 2008-08-11T11:59:25.777 に答える
10

以前、valgrind、purify などの通常の手法をすべて回避するバグがありました。クラッシュは、大量のメモリを搭載したマシンと大規模な入力データ セットでのみ発生しました。

最終的に、デバッガーの監視ポイントを使用して追跡しました。ここで手順を説明します。

1) 失敗の原因を見つけます。サンプルコードから、「exampleString」のメモリが破損しているため、書き込むことができないようです。この仮定を続けましょう。

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

3) 「exampleString」のデータメンバーにウォッチポイントを追加します。私のバージョンの g++ では、文字列は に格納され_M_dataplus._M_pます。このデータ メンバーがいつ変更されるかを知りたいのです。このための GDB 手法は次のとおりです。

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

ここでは明らかに Linux と g++ および gdb を使用していますが、メモリ ウォッチ ポイントはほとんどのデバッガで利用できると思います。

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

gdbwhereコマンドは、変更の結果を示すバック トレースを提供します。これは完全に正当な変更であり、その場合は続行するか、運が良ければメモリ破損による変更になります。後者の場合、実際に問題を引き起こしているコードを確認して、できれば修正できるはずです。

バグの原因は、負のインデックスを使用した配列アクセスでした。インデックスは、配列のサイズを法とする「int」へのポインターのキャストの結果でした。このバグは、valgrind などによって見落とされていました。これらのツールでの実行時に割り当てられたメモリ アドレスは " > MAX_INT" ではなく、負のインデックスになることはありませんでした。

于 2008-09-16T13:06:18.773 に答える
7

問題をデバッグする方法を知りたければ、それは簡単です。まず、死んだニワトリを手に入れます。それから、それを振り始めます。

真剣に、私はこの種のバグを追跡するための一貫した方法を見つけていません。潜在的な問題が非常に多いため、確認すべき簡単なチェックリストはありません。ただし、次のことをお勧めします。

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

他にも試してみるべきことがたくさんあります。きっと他の人たちも同じようにアイデアを出してくれるでしょう。

于 2008-08-11T06:20:21.810 に答える
3

開始するいくつかの場所:

あなたがWindowsを使用していて、ビジュアルC ++ 6を使用している場合(最近誰もそれを使用していないことを願っています)、それは std::string の実装であり、スレッドセーフではなく、この種のことにつながる可能性があります。

これは私が見つけた記事で、メモリリークと破損の一般的な原因の多くを説明しています。

以前の職場では、これを支援するために Compuware Boundschecker を使用していました。それは商業的で非常に高価なので、オプションではないかもしれません.

ここにいくつかの役に立つかもしれない無料のライブラリがあります

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

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

それが役立つことを願っています。メモリの破損は厄介な場所です!

于 2008-08-11T06:49:15.777 に答える
1

ヒープの破損である可能性がありますが、スタックの破損である可能性も同じです。ジムの権利。本当にもう少しコンテキストが必要です。これらの2行のソースは、単独ではあまりわかりません。これを引き起こす原因はいくつもある可能性があります(これがC / C ++の本当の喜びです)。

コードを投稿することに慣れている場合は、すべてのコードをサーバーに配置してリンクを投稿することもできます。そうすればもっとたくさんのアドバイスが得られると思います(そのいくつかは間違いなくあなたの質問とは無関係です)。

于 2008-08-11T05:31:27.213 に答える
1

私が見ることができるようにあなたのコードはエラーがありません。すでに述べたように、より多くのコンテキストが必要です。

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

バグを生成している関数にブレークポイントを設定し、exampleStringの値を確認します。exampleStringに渡すパラメータについても同じようにします。これにより、少なくともstd::stringsが有効かどうかがわかります。

この記事の答えは、ポインターに関する優れたガイドであることがわかりました。

于 2008-08-11T05:38:47.090 に答える
1

コードは、私のプログラムがどこで失敗したかの単なる例です (それはスタックに割り当てられていました、Jim)。私は実際に「何を間違えたのか」を探しているのではなく、「自分が何を間違えたのかをどのように診断するのか」を探しています。男に釣りを教えることなど。質問を見ても、私はそれを十分に明確にしていません。編集機能ありがとうございます。:')

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

 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 次元配列を割り当てたことはありません。うまくいくようです。

于 2008-08-11T06:01:25.610 に答える
1

また、実際に std::string の問題を修正しました。どのように?それをベクトルに置き換え、コンパイルしてから、文字列を再度置き換えます。そこでは一貫してクラッシュしていましたが、それは修正されませんでしたが...できませんでした。そこには何か厄介なものがあり、何がわからない。

それはあなたが本当にチキンを振ったようですね。なぜ今動いているのかわからない場合は、まだ壊れており、後でまた噛まれることはほぼ保証されています (さらに複雑なものを追加した後)。

于 2008-08-11T06:26:06.853 に答える
1

浄化を実行します。

触れてはいけないメモリを壊している、解放しないことでメモリがリークしている、二重解放などを報告する魔法のようなツールです。

マシン コード レベルで動作するため、ソース コードを用意する必要さえありません。

私が参加した中で最も楽しいベンダー電話会議の 1 つは、Purify がコードにメモリ リークを発見したときでした。彼らの声の驚き。

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

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

(かなり高価ですが、無料の評価ダウンロードがあります)

于 2008-08-11T07:24:10.137 に答える
1

私が頻繁に使用するデバッグ手法の 1 つは (最も極端な場合を除きます)、分割統治です。現在、プログラムが特定のエラーで失敗している場合は、何らかの方法でプログラムを半分に分割し、同じエラーがまだ発生しているかどうかを確認してください。明らかに、秘訣はプログラムをどこで分割するかを決めることです!

与えられた例は、エラーがどこにあるのかを判断するのに十分なコンテキストを示していません。他の誰かがあなたの例を試してみれば、うまくいくでしょう。したがって、プログラムで、表示されていない余分なものを可能な限り削除して、それが機能するかどうかを確認してください。その場合は、失敗し始めるまで、他のコードを少しずつ追加します。次に、追加したばかりのものがおそらく問題です。

プログラムがマルチスレッドの場合、おそらくより大きな問題が発生することに注意してください。そうでない場合は、この方法で絞り込むことができるはずです。幸運を!

于 2008-08-11T08:34:56.407 に答える
1

Boundschecker や Purify などのツール以外に、このような問題を解決するための最善の策は、コードを読むのが本当に上手になり、作業しているコードに慣れることです。

メモリの破損は、トラブルシューティングが最も難しいものの 1 つであり、通常、これらのタイプの問題は、デバッガーで何時間も何日も費やして、「ポインター X が削除された後に使用されている!」などのことに気付くことによって解決されます。

それが何かに役立つとすれば、それは経験を積むにつれて上達していくものです。

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

于 2008-08-21T19:18:44.040 に答える
0

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

質問:exampleStringは、newで作成された文字列オブジェクトへのポインタですか?

于 2008-08-11T05:18:52.683 に答える