web-dev-qa-db-ja.com

C ++でのセグメンテーション違反の修正

WindowsとUnix用のクロスプラットフォームC++プログラムを書いています。ウィンドウ側では、コードはコンパイルされ、問題なく実行されます。 Unix側ではコンパイルできますが、実行しようとするとセグメンテーションエラーが発生します。私の最初の予感は、ポインターに問題があるということです。

セグメンテーション障害エラーを見つけて修正するための良い方法は何ですか?

66
Elpezmuerto
  1. -gを使用してアプリケーションをコンパイルすると、バイナリファイルにデバッグシンボルが含まれます。

  2. gdbを使用してgdbコンソールを開きます。

  3. fileを使用して、コンソールでアプリケーションのバイナリファイルを渡します。

  4. runを使用して、アプリケーションの起動に必要な引数を渡します。

  5. セグメンテーションフォールトを引き起こす何かを行います。

  6. btコンソールにgdbと入力して、Segmentation Faultのスタックトレースを取得します。

101
Svisstack

クラッシュ自体が問題の本当の原因ではない場合があります。おそらく、以前の時点でメモリが破壊されたかもしれませんが、破損が現れるまでに時間がかかりました。 valgrind をチェックしてください。これには、ポインターの問題(配列の境界チェックを含む)の多くのチェックがあります。クラッシュが発生した行だけでなく、問題の開始点を示します。

29
paleozogt

問題が発生する前に、可能な限り回避するようにしてください。

  • できるだけ頻繁にコードをコンパイルして実行します。障害のある部分を見つけやすくなります。
  • 低レベル/エラーを起こしやすいルーチンをカプセル化して、メモリを直接操作する必要がほとんどないようにします(プログラムのモデル化に注意してください)
  • テストスイートを維持します。現在何が機能しているか、何が機能していないかなどの概要を把握しておくと、問題の場所を見つけるのに役立ちます( Boost test が可能な解決策です。ドキュメントは、表示する必要がある情報の種類を理解するのに役立ちます。

適切なツールを使用してデバッグします。 Unixの場合:

  • [〜#〜] gdb [〜#〜] は、プログラムがクラッシュした場所を示し、どのコンテキストで表示されるかを示します。
  • Valgrind は、多くのメモリ関連エラーを検出するのに役立ちます。
  • GCCでは、 mudflap も使用できます。 GCCおよびClangでは、 アドレス/メモリサニタイザー を使用できます。 Valgrindで検出されないエラーを検出でき、パフォーマンスの低下がより少なくなります。

最後に、いつものことをお勧めします。プログラムが読みやすく、保守しやすく、明快で整頓されているほど、デバッグが簡単になります。

16
log0

Unixでは、valgrindを使用して問題を見つけることができます。無料で強力です。自分でやりたい場合は、newおよびdelete演算子をオーバーロードして、各新しいオブジェクトの前後に0xDEADBEEFを持つ1バイトの構成をセットアップできます。次に、各反復で何が起こるかを追跡します。これはすべてをキャッチするのに失敗する可能性があります(これらのバイトに触れることさえ保証されていません)が、過去にWindowsプラットフォームで働いていました。

3
wheaties

はい、ポインターに問題があります。適切に初期化されていないものを使用している可能性は非常に高いですが、メモリ管理が二重解放などで台無しになっている可能性もあります。

ローカル変数としての初期化されていないポインターを避けるために、できれば意味のある値で初期化できる場合は、できる限り遅く宣言してください(これは常に可能であるとは限りません)。コードを調べて、使用される前に値があることを確信してください。それが困難な場合は、それらをヌルポインター定数(通常はNULLまたは_0_として記述)に初期化し、チェックします。

メンバー値として初期化されていないポインターを避けるには、コンストラクターで適切に初期化され、コンストラクターと代入演算子のコピーで適切に処理されることを確認してください。メモリ管理のためにinit関数に依存しないでください。ただし、他の初期化は可能です。

クラスにコピーコンストラクターまたは代入演算子が必要ない場合は、プライベートメンバー関数として宣言し、定義しないでください。明示的または暗黙的に使用されている場合、コンパイラエラーが発生します。

該当する場合は、スマートポインターを使用します。ここでの大きな利点は、それらに固執して一貫して使用すれば、deleteの記述を完全に回避でき、二重削除されないことです。

Cスタイルの文字列と配列の代わりに、可能な場合は常にC++文字列とコンテナクラスを使用します。 _[i]_ではなく.at(i)を使用することを検討してください。これは境界チェックを強制するためです。少なくともデバッグモードで_[i]_の境界をチェックするようにコンパイラまたはライブラリを設定できるかどうかを確認してください。セグメンテーション違反は、完全に適切なポインターにガベージを書き込むバッファーオーバーランによって発生する可能性があります。

これらのことを行うと、セグメンテーションエラーやその他のメモリの問題の可能性が大幅に減少します。彼らは間違いなくすべてを修正するのに失敗するでしょう、そしてそれはあなたが時々問題を持たないときvalgrindを使い、そうするときvalgrindとgdbを使うべきである理由です。

2
David Thornley

このようなことを修正するために使用する方法論は知りません。プログラムの振る舞いが定義されていないという問題があるため、どちらかを考え出すことは不可能だと思います。

問題が発生する前に回避するためのあらゆる種類の「方法論」があります。 1つの重要なものはRAIIです。

それに加えて、あなたは最高の精神的なエネルギーをそれに投げるだけです。

0
Edward Strange