WindowsとUnix用のクロスプラットフォームC++プログラムを書いています。ウィンドウ側では、コードはコンパイルされ、問題なく実行されます。 Unix側ではコンパイルできますが、実行しようとするとセグメンテーションエラーが発生します。私の最初の予感は、ポインターに問題があるということです。
セグメンテーション障害エラーを見つけて修正するための良い方法は何ですか?
-g
を使用してアプリケーションをコンパイルすると、バイナリファイルにデバッグシンボルが含まれます。
gdb
を使用してgdbコンソールを開きます。
file
を使用して、コンソールでアプリケーションのバイナリファイルを渡します。
run
を使用して、アプリケーションの起動に必要な引数を渡します。
セグメンテーションフォールトを引き起こす何かを行います。
bt
コンソールにgdb
と入力して、Segmentation Faultのスタックトレースを取得します。
クラッシュ自体が問題の本当の原因ではない場合があります。おそらく、以前の時点でメモリが破壊されたかもしれませんが、破損が現れるまでに時間がかかりました。 valgrind をチェックしてください。これには、ポインターの問題(配列の境界チェックを含む)の多くのチェックがあります。クラッシュが発生した行だけでなく、問題の開始点を示します。
問題が発生する前に、可能な限り回避するようにしてください。
適切なツールを使用してデバッグします。 Unixの場合:
最後に、いつものことをお勧めします。プログラムが読みやすく、保守しやすく、明快で整頓されているほど、デバッグが簡単になります。
Unixでは、valgrindを使用して問題を見つけることができます。無料で強力です。自分でやりたい場合は、newおよびdelete演算子をオーバーロードして、各新しいオブジェクトの前後に0xDEADBEEF
を持つ1バイトの構成をセットアップできます。次に、各反復で何が起こるかを追跡します。これはすべてをキャッチするのに失敗する可能性があります(これらのバイトに触れることさえ保証されていません)が、過去にWindowsプラットフォームで働いていました。
はい、ポインターに問題があります。適切に初期化されていないものを使用している可能性は非常に高いですが、メモリ管理が二重解放などで台無しになっている可能性もあります。
ローカル変数としての初期化されていないポインターを避けるために、できれば意味のある値で初期化できる場合は、できる限り遅く宣言してください(これは常に可能であるとは限りません)。コードを調べて、使用される前に値があることを確信してください。それが困難な場合は、それらをヌルポインター定数(通常はNULL
または_0
_として記述)に初期化し、チェックします。
メンバー値として初期化されていないポインターを避けるには、コンストラクターで適切に初期化され、コンストラクターと代入演算子のコピーで適切に処理されることを確認してください。メモリ管理のためにinit
関数に依存しないでください。ただし、他の初期化は可能です。
クラスにコピーコンストラクターまたは代入演算子が必要ない場合は、プライベートメンバー関数として宣言し、定義しないでください。明示的または暗黙的に使用されている場合、コンパイラエラーが発生します。
該当する場合は、スマートポインターを使用します。ここでの大きな利点は、それらに固執して一貫して使用すれば、delete
の記述を完全に回避でき、二重削除されないことです。
Cスタイルの文字列と配列の代わりに、可能な場合は常にC++文字列とコンテナクラスを使用します。 _[i]
_ではなく.at(i)
を使用することを検討してください。これは境界チェックを強制するためです。少なくともデバッグモードで_[i]
_の境界をチェックするようにコンパイラまたはライブラリを設定できるかどうかを確認してください。セグメンテーション違反は、完全に適切なポインターにガベージを書き込むバッファーオーバーランによって発生する可能性があります。
これらのことを行うと、セグメンテーションエラーやその他のメモリの問題の可能性が大幅に減少します。彼らは間違いなくすべてを修正するのに失敗するでしょう、そしてそれはあなたが時々問題を持たないときvalgrindを使い、そうするときvalgrindとgdbを使うべきである理由です。
このようなことを修正するために使用する方法論は知りません。プログラムの振る舞いが定義されていないという問題があるため、どちらかを考え出すことは不可能だと思います。
問題が発生する前に回避するためのあらゆる種類の「方法論」があります。 1つの重要なものはRAIIです。
それに加えて、あなたは最高の精神的なエネルギーをそれに投げるだけです。