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