web-dev-qa-db-ja.com

C ++:ゼロ除算エラーをキャッチ

以下は、ゼロによる除算が発生する簡単なコードです。私はそれをキャッチしようとしています:

#include <iostream>

int main(int argc, char *argv[]) {
    int Dividend = 10;
    int Divisor = 0;

    try {
        std::cout << Dividend / Divisor;
    } catch(...) {
        std::cout << "Error.";
    }
    return 0;
}

しかし、とにかくアプリケーションはクラッシュします(オプション-fexceptions ofMinGW)。

そのような例外をキャッチすることは可能ですか(C++の例外ではなく、FPUの例外であると理解しています)?

私は除数before除算をチェックできることを知っていますが、ゼロによる除算はまれなので(少なくとも私のアプリでは)、試してみるのがより効率的であると仮定しました分割する前に除数を毎回テストするよりも、分割(およびエラーが発生した場合はエラーをキャッチ)します。

WindowsXPコンピューターでこれらのテストを行っていますが、クロスプラットフォームにしたいと思います。

27
Jérôme

例外ではありません。 エラーであり、これはハードウェアレベルで決定され、オペレーティングシステムに返されます。その後、OS固有の方法でプログラムに通知します(強制終了など)。プロセス)。

I believeそのような場合、起こることは例外ではなくシグナルです。その場合:オペレーティングシステムは、プログラムのメイン制御フローを中断し、シグナルハンドラーを呼び出します。これにより、プログラムの操作が終了します。

これは、nullポインターを逆参照するときに表示されるのと同じタイプのエラーです(プログラムは、SIGSEGVシグナル、セグメンテーションフォールトによってクラッシュします)。

<csignal>ヘッダーの関数を使用して、SIGFPEシグナルのカスタムハンドラーを提供しようとすることができます(浮動小数点の例外用ですが、ゼロによる整数除算でも発生する場合があります-I ' mここでは本当にわかりません)。ただし、信号処理はOS依存であり、MinGWはWindows環境でPOSIX信号を何らかの方法で「エミュレート」することに注意してください。


MinGW 4.5、Windows 7でのテストは次のとおりです。

#include <csignal>
#include <iostream>

using namespace std;

void handler(int a) {
    cout << "Signal " << a << " here!" << endl;
}

int main() {
    signal(SIGFPE, handler);
    int a = 1/0;
}

出力:

シグナル8はこちら!

そして、シグナルハンドラを実行した直後に、システムはプロセスを強制終了し、エラーメッセージを表示します。

これを使用すると、ゼロによる除算またはnullポインター逆参照の後にリソースを閉じるか、エラーをログに記録できます... 例外が例外的な場合でもプログラムのフローを制御する方法ではない例外とは異なります A有効なプログラムはそれを行うべきではありません。これらの信号をキャッチすることは、デバッグ/診断の目的でのみ役立ちます。

(低レベルのプログラミングで一般的に非常に有用で、ハンドラーの直後にプログラムを強制終了させないいくつかの有用なシグナルがありますが、それは深いトピックです)

32
Kos

ゼロによる除算は論理的なエラーであり、プログラマーのバグです。あなたはそれに対処しようとするべきではありません、あなたはそれをデバッグして排除するべきです。さらに、例外のキャッチは、除数チェックよりも非常に高価です。

構造化例外処理を使用して、ゼロ除算エラーをキャッチできます。達成方法は、コンパイラによって異なります。 MSVCは、構造化例外をcatch(...)としてキャッチする関数を提供し、構造化例外を通常の例外に変換する関数を提供し、__try/__except/__finally。ただし、MinGWについては、そのコンパイラでそれを行う方法を十分に理解していません。

13
Puppy
  1. CPUからゼロ除算をキャッチする言語標準の方法はありません。

  2. ブランチを時期尚早に「最適化」しないでください。あなたのアプリケーションは実際にこのコンテキストでCPUバウンドですか?私はそれを疑います。そして、あなたのコードを壊すならば、それは本当に最適化ではありません。それ以外の場合は、コードをさらに高速化できます。

    int main(int argc, char *argv[]) { /* Fastest program ever! */ }
    
8
Greg D

C++ではゼロ除算は例外ではありません。 https://web.archive.org/web/20121227152410/http://www.jdl.co.uk/briefings/divByZeroInCpp.html を参照してください。

6
ismail

どういうわけか、本当の説明はまだ欠落しています。

そのような例外をキャッチすることは可能ですか(C++の例外ではなく、FPUの例外であると理解しています)?

はい、あなたのcatchブロックはいくつかのコンパイラで動作するはずです。しかし、問題はあなたの例外FPU例外ではないであるということです。整数除算を行っています。これもキャッチ可能なエラーであるかどうかはわかりませんが、浮動小数点数のIEEE表現の機能を使用するFPU例外ではありません。

3
Konrad Rudolph

Windows(Visual C++を使用)では、これを試してください:

BOOL SafeDiv(INT32 dividend, INT32 divisor, INT32 *pResult)
{
    __try 
    { 
        *pResult = dividend / divisor; 
    } 
    __except(GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO ? 
             EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
    { 
        return FALSE;
    }
    return TRUE;
}

MSDN: http://msdn.Microsoft.com/en-us/library/ms681409(v = vs.85).aspx

1
jalal sadeghi

無限の「Signal 8 here!」を避けるためにメッセージ、Kos Niceコードに 'exit'を追加するだけです:

#include <csignal>
#include <iostream>
#include <cstdlib> // exit

using namespace std;

void handler(int a) {
    cout << "Signal " << a << " here!" << endl;
    exit(1);
}

int main() {
    signal(SIGFPE, handler);
    int a = 1/0;
}
0
nibrot

さて、これについて例外処理があった場合、実際にはいくつかのコンポーネント実行する必要があるチェック。したがって、自分でチェックしても何も失われません。そして、それは単純な比較文よりも高速ではありません(1つのCPU命令「ゼロに等しい場合にジャンプ」またはそのようなもの、名前を覚えていない)

0
Atmocreations