アクセス違反の例外をキャッチしますか?


89

int *ptr;
*ptr = 1000;

Microsoft固有のものを使用せずに、標準のC ++を使用してメモリアクセス違反の例外をキャッチできますか。

回答:


42

いいえ。C ++は、何か悪いことをしたときに例外をスローしません。これにより、パフォーマンスが低下します。アクセス違反やゼロ除算エラーのようなものは、キャッチできる言語レベルのものというよりも、「マシン」例外のようなものです。


HWの例外であることは知っていますが、これを処理するMicrosoft固有のキーワードがあります(__try __except)?
Ahmed Said

2
@Ahmed:はい。ただし、それらを使用すると、「不可能な」ことが起こる可能性があります。たとえば、コードのAV行ののステートメントの一部がすでに実行されている場合や、AVの前のステートメントが実行されていない場合があります。
アーロン

VC ++で通常のtry ... catchブロックを使用してこのような例外処理を有効にする方法については、以下の私の回答を参照してください。
Volodymyr Frytskyy 2013年

@Aaron「起こり得ないことが起こっている」部分について詳しく説明していただけますか?コンパイラやCPUの並べ替え命令が原因ですか?
WeipengL18年

基盤となるオペレーティングシステムは、このような問題をキャッチするメカニズムを提供することが多く、例外はCPUアーキテクチャによって生成されるため、コストは発生しません。これは、デバッガーが例外をトラップして、コードの実行を遅くすることなくデバッグできるようにする方法によって証明されています。
ディーノディニ

108

それを読んで泣いてください!

私はそれを考え出した。ハンドラーからスローしない場合、ハンドラーは続行するだけで、例外も続行します。

あなたがあなた自身の例外を投げてそれを処理するとき、魔法は起こります。

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <tchar.h>

void SignalHandler(int signal)
{
    printf("Signal %d",signal);
    throw "!Access Violation!";
}

int main()
{
    typedef void (*SignalHandlerPointer)(int);

    SignalHandlerPointer previousHandler;
    previousHandler = signal(SIGSEGV , SignalHandler);
    try{
        *(int *) 0 = 0;// Baaaaaaad thing that should never be caught. You should write good code in the first place.
    }
    catch(char *e)
    {
        printf("Exception Caught: %s\n",e);
    }
    printf("Now we continue, unhindered, like the abomination never happened. (I am an EVIL genius)\n");
    printf("But please kids, DONT TRY THIS AT HOME ;)\n");

}

特に__try / __exceptはAVもキャッチしないので、良いヒントです。
ファビオセコネロ2011

16
これはgccでは機能しませんが、VC ++では機能しますが、「デバッグ」ビルドでのみ機能します。まだ興味深い解決策に賛成しています。シグナルハンドラーが呼び出されますが、例外はスローされません。
ナタリーアダムス

2
それはポータブルでは機能しません。シグナルハンドラが呼び出されたとき、スタックフレームとレジスタの変更は、通常の関数スタックフレームのときと同じではありません(一部のシステムでは同じスタックを使用しない場合もあります)。最善の方法は、シグナルハンドラがアクティブになったことを示すフラグを設定することです。次に、コードでそのフラグをテストしてスローします。
マーティンヨーク

2
これにより、未定義の動作が発生する可能性が高くなります。これがPOSIXで機能するためには、代替のシグナルスタック(sigaltstack)がインストールされていない必要があり(C ++例外アンワインド実装で許可されている場合を除く)、アンワインドメカニズム自体を処理するすべてのランタイム関数はシグナルセーフである必要があります。
minmaxavg 2017年

1
:あなたは(この場合はSIGSEGV)信号に、デフォルトのハンドラを返したい場合は、日科技連、次の使用signal(SIGSEGV, SIG_DFL);
kocica

67

try- > catch(...)ブロックを使用して、Visual Studioであらゆる種類の例外(ゼロ除算、アクセス違反など)をキャッチする非常に簡単な方法があります。マイナーなプロジェクト設定の調整で十分です。プロジェクト設定で/ EHaオプションを有効にするだけです。「プロジェクトのプロパティ」->「C / C ++」->「コード生成」->「C ++例外を有効にする」を「SEH例外ではい」に変更するを参照してください。それでおしまい!

詳細はこちらをご覧ください:http//msdn.microsoft.com/en-us/library/1deeycx5(v = vs.80).aspx


Visual Studio .NET 2003にはそのような設定値はなく、「いいえ」と「はい(/ EHsc)」のみがあります。この設定を有効にするために必要なVisualStudioの最小バージョンを明確にできますか?
izogfif 2014

リンクは「VisualStudio2005」を指定しているようです
Drew Delano 2015

2
gccまたはMinGWの場合はどうなりますか?
user1024 2018

10

少なくとも私にとっては、signal(SIGSEGV ...)別の回答で言及されているアプローチは、Visual C ++ 2015を使用するWin32では機能しませんでした。私にとってうまくいったのは、にあるを使用する_set_se_translator()ことeh.hでした。それはこのように動作します:

ステップ1Volodymyr Frytskyyの回答に記載されているように、プロジェクトプロパティ/ C ++ /コード生成/ C ++例外の有効化でSEH例外(/ EHa)を使用して[はい]を有効にしていることを確認します。

ステップ2)を呼び出し_set_se_translator()、新しい例外トランスレータの関数ポインタ(またはラムダ)を渡します。これは、基本的に低レベルの例外を取得し、次のようにキャッチしやすいものとして再スローするため、トランスレータと呼ばれstd::exceptionます。

#include <string>
#include <eh.h>

// Be sure to enable "Yes with SEH Exceptions (/EHa)" in C++ / Code Generation;
_set_se_translator([](unsigned int u, EXCEPTION_POINTERS *pExp) {
    std::string error = "SE Exception: ";
    switch (u) {
    case 0xC0000005:
        error += "Access Violation";
        break;
    default:
        char result[11];
        sprintf_s(result, 11, "0x%08X", u);
        error += result;
    };
    throw std::exception(error.c_str());
});

ステップ3)通常どおりに例外をキャッチします。

try{
    MakeAnException();
}
catch(std::exception ex){
    HandleIt();
};

1
このサイトは私のために_set_se_translator()メソッドと作品、上の例の簡単なカップルが含まmsdn.microsoft.com/en-us/library/5z4bw5h5.aspx
Pabitraダッシュ

8

このタイプの状況は実装に依存するため、トラップするにはベンダー固有のメカニズムが必要になります。Microsoftの場合、これにはSEHが含まれ、* nixにはシグナルが含まれます。

一般に、アクセス違反の例外をキャッチすることは非常に悪い考えです。AV例外から回復する方法はほとんどありません。回復しようとすると、プログラムのバグを見つけるのが難しくなります。


1
だからあなたのアドバイスは、AV例外の原因を知ることですよね?
Ahmed Said

4
絶対に。AVはコードのバグを表しており、例外をキャッチすると問題が隠されます。
JaredPar 2009年

1
明確にするために、C ++標準では、未定義、未指定、および実装定義を区別しています。実装定義とは、実装で何が行われるかを指定する必要があることを意味します。問題のコードは未定義です。つまり、何かが発生する可能性があり、毎回異なる可能性があります。
KeithB 2009年

15
アクセス違反をキャッチすることは悪い考えではありません-それはユーザーエクスペリエンスにとって良いことです。ただし、この場合に私が行う唯一の意味のあることは、バグレポートGUIを使用して別のプロセスを生成し、現在のプロセスダンプを作成することです。プロセスの生成は常に成功する操作です。次に、TerminateProcess()を実行して自殺します。
ПетърПетров

12
例外をキャッチして黙って無視するのは悪い考えです。可能であれば、例外をキャッチし、診断目的でアプリケーションの状態に関する情報を記録することをお勧めします。私はかつて、デバッグが必要なバックエンドグラフィックライブラリのUIを作成しました。クラッシュするたびに、私がUIを書いたことを知っていたので、人々が私のところにやって来ました。ライブラリがクラッシュしたことをユーザーに通知するアラートをポップアップするバックエンドの周りにsigトラップを配置しました。人々は図書館の著者に行き始めました。
ケント

8

述べたように、Windowsプラットフォームでこれを行うMicrosoft /コンパイラベンダー以外の方法はありません。ただし、これらのタイプの例外を通常のtry {} catch(exception ex){}の方法でキャッチして、エラーを報告し、アプリをより適切に終了することは明らかに便利です(JaredParが言うように、アプリはおそらく問題を抱えています) 。単純なクラスラッパーで_se_translator_functionを使用して、tryハンドラーで次の例外をキャッチできるようにします。

DECLARE_EXCEPTION_CLASS(datatype_misalignment)
DECLARE_EXCEPTION_CLASS(breakpoint)
DECLARE_EXCEPTION_CLASS(single_step)
DECLARE_EXCEPTION_CLASS(array_bounds_exceeded)
DECLARE_EXCEPTION_CLASS(flt_denormal_operand)
DECLARE_EXCEPTION_CLASS(flt_divide_by_zero)
DECLARE_EXCEPTION_CLASS(flt_inexact_result)
DECLARE_EXCEPTION_CLASS(flt_invalid_operation)
DECLARE_EXCEPTION_CLASS(flt_overflow)
DECLARE_EXCEPTION_CLASS(flt_stack_check)
DECLARE_EXCEPTION_CLASS(flt_underflow)
DECLARE_EXCEPTION_CLASS(int_divide_by_zero)
DECLARE_EXCEPTION_CLASS(int_overflow)
DECLARE_EXCEPTION_CLASS(priv_instruction)
DECLARE_EXCEPTION_CLASS(in_page_error)
DECLARE_EXCEPTION_CLASS(illegal_instruction)
DECLARE_EXCEPTION_CLASS(noncontinuable_exception)
DECLARE_EXCEPTION_CLASS(stack_overflow)
DECLARE_EXCEPTION_CLASS(invalid_disposition)
DECLARE_EXCEPTION_CLASS(guard_page)
DECLARE_EXCEPTION_CLASS(invalid_handle)
DECLARE_EXCEPTION_CLASS(microsoft_cpp)

元のクラスは、この非常に役立つ記事からのものです。

http://www.codeproject.com/KB/cpp/exception.aspx


8
Microsoftコンパイラの使用は、違法な命令またはアクセス違反と同じように扱われるようです。面白い。
デビッドソーンリー

3

例外処理メカニズムではありませんが、Cによって提供されるsignal()メカニズムを使用できます。

> man signal

     11    SIGSEGV      create core image    segmentation violation

NULLポインターへの書き込みは、おそらくSIGSEGVシグナルを引き起こします。


@maidamaisignal()はposix標準の一部です。Windowsは、POSIX標準(LinuxおよびUNIXがするように)を実装
マーティンニューヨーク

-1

そのような違反は、コードに重大な問題があり、信頼性が低いことを意味します。ユーザーのデータがまだ破損していないことを期待して、プログラムが以前のデータを上書きしないことを期待する方法でユーザーのデータを保存しようとする可能性があることがわかりますが、定義上、標準的な方法はありません未定義の振る舞いに対処すること。


6
アクセス違反からの回復が可能な場合があります。EIPジャンプボイレーションからの回復は、危険を冒してアセンブリレベルの命令ポインタを保持しない限り不可能です。ただし、アクセス違反をキャッチすることは、バグレポートGUI機能の別のプロセスを生成するのに適しています。
ПетърПетров
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.