ありません try-catch
ブロックキャッチセグメンテーション違反エラー?
以下の関数を使用してテキストファイルを読み取っていますが、ファイルが空でプログラムがクラッシュすることがあります。このファイルが空または使用中の場合、プログラムを実行し続け、別のファイルを提供してほしい。
Path2D read_gesture(const char* filename)
{
Path2D path;
//MultiStrokeGesture MultiStrokes;
vector<string> text_file;
int no_of_paths=0;
std::ifstream ifs(filename);
for (std::string line; std::getline(ifs, line); )
{
no_of_paths=no_of_paths+1;
double a, b;
stringstream ss(line);
if (!(ss >> a >> b)) {cout<<"wrong format"<<endl;}
std::cout << "You said, " << a << ", " << b << ".\n";
path.Push_back(Point2D(a,b));
}
cout<<"saving gesture"<<endl;
return path;
}
私は次のようなことを試しました:
Path2D path;
try
{
path=read_gesture("test.txt");
}
catch(int e)
{
path=read_gesture("test2.txt");
}
しかし、プログラムはまだクラッシュします。問題は何でしょうか?
catch
で呼び出されたファイルはtry
のファイルと同じではなく、タイプミスでした。C++ try-catch
ブロックはC++例外のみを処理します。セグメンテーション違反などのエラーは低レベルであり、try-catchはこれらのイベントを無視し、try-catchブロックがない場合と同じように動作します。
try/catchは、C++例外のみをキャッチします。セグメンテーション違反は、プログラムが不正な処理を実行し、未定義の動作を呼び出した場合にのみ発生します。
未定義の動作は、クラッシュしないなど、さまざまな形で現れる可能性があることに注意してください。修正が必要なことがあることを通知するためにプログラムをクラッシュさせることができて幸運ですが、プログラムはクラッシュしない可能性があります。フォールバックコードをクラッシュに依存させることはできません。
適切なことは、例外のようにクラッシュを処理するのではなく、入力が期待どおりでない場合でもプログラムが違法なことを行わないようにすることです。この場合、ファイルが空であることがわかり、別のファイルを提供する必要があるように、コードを変更する必要があります。
通常、セグメンテーション違反を処理する方法がありますが、それはあなたが探している種類の回復を行うことを意図していません。メカニズムはシグナルです。セグメンテーション違反のSIGSEGVなど、指定されたシグナルが発生したときに実行されるシグナルハンドラーをインストールできます。ただし、std :: raiseで明示的に上げる場合を除いて、このようなシグナルが実際に発生する必要はありません。また、実装によってシグナルが発生したときにシグナルハンドラーで実行できることは、厳しく制限されています。
アボートまたはレイズ関数を呼び出した結果以外の結果としてシグナルが発生した場合、シグナルハンドラーが、volatile sig_atomic_tとして宣言されたオブジェクト、またはシグナルに値を割り当てる以外の静的ストレージ期間を持つオブジェクトを参照すると、動作は定義されません。ハンドラーは、アボート関数、_Exit関数、またはハンドラーの呼び出しを引き起こしたシグナルに対応するシグナル番号に等しい最初の引数を持つシグナル関数以外の標準ライブラリ内の関数を呼び出します。さらに、このようなシグナル関数の呼び出しによってSIG_ERRが返される場合、errnoの値は不確定です。
プログラムにセグメンテーション違反があり、それが意図的に行ったものではない場合、それについてできることは何もありません。捕まえられず、捕まえたとしても、その後はプログラムを続けることができません。
さらに、次のコードには非常に深刻な問題があります。
try {
path=read_gesture("test.txt");
}
catch(int e) {
path=read_gesture("test.txt");
}
「最初はうまくいかない場合は、もう一度やり直してください」は人間にとって良いモットーですが、コンピューターは毎回まったく同じように動作します。操作が失敗した場合、それが一時的な障害(ネットワーク障害など)でない限り、再試行は無駄です。
正しいプログラムはセグメンテーション違反を起こさないため、正しいプログラムを作成するしかありません。セグメンテーション違反を見つけたい場合は、ValgrindまたはGDBでプログラムを実行できます。どちらも、手がかりでいっぱいのバックトレースを提供するはずです(ただし、プログラムの真のエラーを見つけるには、頭を使う必要があります)。
もう1つの方法は、Java、C#、Python、Ruby、Haskell、JavaScript、Go、Rust、またはCやC++を除く最近使用されているほとんどすべての言語など、segfaultを取得しない言語を使用することです。
脚注:セグメンテーション違反を起こす正しいプログラムを実際に書くことができるので、これは少し単純化されています。しかし、それはあなたがしていることではありません。
まず第一に、セグメンテーション違反などの例外を決して生成しない方法でコードを書くことができます(読むべきです)。
まず、無効である可能性のあるすべてのポインターをチェックする必要があります(たとえば、クラスのユーザーによって呼び出されるクラスのパブリック関数は、無効なポインターを受け取る場合がありますが、内部実装では、ポインターが既にチェックされていると見なす場合があります)。
次に、失敗する可能性のある関数の結果を確認する必要があります。たとえば、malloc
がメモリの割り当てに失敗したり、開こうとしたファイルが削除されたり、開く権限がなかったり、開いた後でもデータが無効である可能性があるため、アクションの結果を確認する必要があります。 C++でのこのプロセスは、Cよりもはるかに簡単です。たとえば、失敗時にnew
が例外をスローするか、エラーまたはeofが発生した場合にstream
をfalse
に変換できます。
しかし、一般的にあなたの質問に答えるために、catch
ブロックは型指定された例外のみをキャッチしますが、一部のコンパイラではcatch(...)
はセグメンテーション障害などの例外をキャッチしたり、それらの例外をC++例外に変換したりすることもあります。 _set_se_translator
を使用して、そのような例外のグローバルトランスレータをC++例外に設定できます。また、MSVCを使用して、プログラムを/EHa
でコンパイルし、catch(...)
でそのような種類の例外をキャッチできるようにすることもできます。これらはすべて、特定のプラットフォームまたはコンパイラに固有の拡張機能であるため、コードを正しく記述し、問題を解決するためのそのような方法を考えないでください。
少しテストを追加して、ifsが有効かどうかを確認できます。
#include <iostream>
#include <fstream>
int main(int argc, char * argv[]){
std::ifstream ifs( "notExists" );
if( ifs.is_open()) {
char line[1024];
while(! ifs.fail() && ifs.getline( line, sizeof( line ))) {
std::cout << line << std::endl;
}
}
else {
std::cout << "file doesn't exists." << std::endl;
}
std::cout << "done." << std::endl;
return 0;
}
このプログラムが実行され、出力されます。
file doesn't exists.
done.
bool std :: ifstream :: is_open();
詳細については、 getline グローバル関数を参照してください。失敗した場合は、ここでチェックされていないビットが設定されます。
エラーは、内部状態フラグを変更することによって通知されます。
さらに、これらのいずれの場合でも、isのメンバー関数ios :: exceptionsで適切なフラグが設定されていると、タイプios_base :: failureの例外がスローされます。
catch(...){cout<<'catched';}
でキャッチできるかどうかを確認してください
また、この行を試してください。これにより、間違ったフォーマットポイントをプッシュするのを防ぐことができます。
if (!(ss >> a >> b)) {cout<<"wrong format"<<endl; continue;}