質問

それで、助けが必要です。私は 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 には問題ありません)。私が書いたコードをチェックすれば、賢明な皆さんならすぐに問題に気づくと思いますが、私はこの種のコード分析を、いわば私の「ツールボックス」に追加したいと考えています。

役に立ちましたか?

解決

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

  1. 私のことを見守っててね ヒープ破損の質問 - 回答が得られ次第、更新していきます。まずはバランス調整でした new[] そして delete[], しかし、あなたはすでにそれをやっています。
  2. 与える ヴァルグリンド もっとやってみよう。これは素晴らしいツールなので、Windows で利用できるようになればいいのにと思います。プログラムの速度は約半分に低下するだけで、Windows の同等のプログラムと比較するとかなり良好です。
  3. の使用を検討してください。 Google パフォーマンス ツール malloc/new の代替品として。
  4. すべてのオブジェクト ファイルを削除して最初からやり直しましたか?おそらくメイクファイルは...「最適ではない」
  5. あなたではない assert()コード内で十分です。見ずにどうやってそれを知ることができますか?デンタルフロスのようなもの、誰も assert()彼らのコードでは十分です。オブジェクトの検証関数を追加し、メソッドの開始時とメソッドの終了時にそれを呼び出します。
  6. あなたは コンパイル -wall?そうでない場合は、そうしてください。
  7. 次のような lint ツールを見つけてください PC-Lint. 。あなたのような小さなアプリは、 PC-lint デモ ページでは、購入する必要はありません。
  8. ポインタを削除した後に NULL 化していることを確認してください。ぶら下がったポインタを好む人はいません。宣言されているが割り当てられていないポインターを持つ同じギグ。
  9. 配列の使用をやめてください。使う ベクター その代わり。
  10. 生のポインタは使用しないでください。使う スマートポインター. 。使用しないでください auto_ptr!それは...驚くべきこと。そのセマンティクスは非常に奇妙です。代わりに、次のいずれかを選択します。 スマート ポインターを強化する, 、またはそのうちの何か ロキ図書館.

他のヒント

かつて、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

gdb where コマンドは、変更の結果を示すバック トレースを提供します。これは完全に合法な変更であり、その場合はそのまま続行するか、運が良ければメモリ破損による変更である可能性があります。後者の場合、次のコードを確認できるようになります。 本当に 問題の原因となっており、できれば修正してください。

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

問題をデバッグする方法を知りたい場合は、それは簡単です。まず、死んだ鶏を入手します。それから、 それを振り始めます.

真剣に言うと、この種のバグを追跡する一貫した方法が見つかりませんでした。潜在的な問題が非常に多くあるため、実行するための単純なチェックリストはありません。ただし、次のことをお勧めします。

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

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

いくつかの場所から始めましょう:

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

それが役立つことを願っています。メモリ破損は最悪の状況です。

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

コードを投稿することに抵抗がない場合は、コードをすべてサーバーに放り込んでリンクを投稿することもできます。そうすれば、より多くのアドバイスが得られると思います(その中には間違いなくあなたの質問と無関係なアドバイスもあります)。

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

また、実際に 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 次元配列を割り当てたことがありませんでした。効果があるようです。

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

それはあなたが本当にチキンを振ったように聞こえます。分からない場合 なぜ 今は動いていても、まだ壊れたままで、後で (さらに複雑さを加えた後) 再び問題を起こすことはほぼ確実です。

浄化を実行します。

これは、触れてはいけないメモリの破壊、解放しないことによるメモリのリーク、二重解放などを行っている場合に報告してくれる、魔法に近いツールです。

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

私がこれまで参加した中で最も楽しかったベンダー電話会議の 1 つは、Purify がコード内でメモリ リークを発見したときでした。そこで「関数 foo() でメモリを解放していない可能性はありますか?」と質問し、その意見を聞くことができました。彼らの声には驚きがあった。

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

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

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

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

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

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

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

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

それが少しでも役立つとしたら、それは経験を積むにつれて上達するものです。

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

ご覧のとおり、コードにエラーはありません。すでに述べたように、より多くのコンテキストが必要です。

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

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

から答えを見つけました この記事 ポインタに関する良いガイドとなるでしょう。

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

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

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top