捕获访问冲突异常?


Answers:


42

不。当您做不好的事情时,C ++不会引发异常,这会导致性能下降。诸如访问冲突或除以零错误之类的事情更像是“机器”异常,而不是您可以捕获的语言级别的事情。


我知道这是硬件异常,但是有Microsoft特定的关键字可以处理此问题(__try __except)?
艾哈迈德·赛义德

2
@Ahmed:是的,但是如果使用它们,可能会发生“不可能”的事情。例如,在AV代码行之后的某些语句可能已经执行,或者在AV行之前的语句未执行。
亚伦,2009年

请参阅下面的答案,如何使用VC ++中的常规try ... catch块启用此类异常处理。
Volodymyr Frytskyy

@Aaron您能否详细说明“发生的不可能的事情”部分?是因为编译器和/或CPU重新排序指令?
Weipeng L

底层操作系统通常会提供捕获此类问题的机制,并且它们不会产生任何成本,因为由CPU体系结构生成的例外情况。调试器能够捕获异常以允许您进行调试而又不减慢代码执行速度的方式证明了这一点。
Dino Dini

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。
Fabio Ceconello 2011年

16
这在gcc中不起作用,但在VC ++中起作用,但仅在“ Debug”构建中起作用。仍在争取一个有趣的解决方案。信号处理程序将被调用,但不会引发异常。
Natalie Adams

2
那不方便。当信号处理程序被调用时,堆栈帧和寄存器计数与普通函数堆栈帧不同(在某些系统上甚至可能不使用同一堆栈)。最好的办法是设置一个标志,以指示信号处理程序已被激活。然后在代码中测试该标志并抛出。
马丁·约克

2
这极有可能引入未定义的行为。为了sigaltstack使它能够在POSIX上运行,必须没有安装任何替代的信号堆栈()(除非C ++异常展开实现允许),并且处理展开机制本身的每个运行时函数都应该是信号安全的。
minmaxavg

1
如果要返回默认处理程序以发出信号(在这种情况下为SIGSEGV),请使用以下命令:signal(SIGSEGV, SIG_DFL);
kocica

67

使用try-> catch(...)块,在Visual Studio中有一种非常简单的方法来捕获任何类型的异常(零除,访问冲突等)。较小的项目设置调整就足够了。只需在项目设置中启用/ EHa选项。请参见项目属性-> C / C ++->代码生成->将启用C ++异常修改为“是,并带有SEH异常”。而已!

在此处查看详细信息:http : //msdn.microsoft.com/zh-cn/library/1deeycx5(v=vs.80).aspx


在Visual Studio .NET 2003中没有这样的设置值,只有“否”和“是(/ EHsc)”。您能否阐明启用该设置所需的最低Visual Studio版本?
izogfif 2014年

该链接似乎指定了“ Visual Studio 2005”
Drew Delano

2
如果使用gcc或MinGW怎么办?
user1024

10

至少对我来说,signal(SIGSEGV ...)另一个答案中提到的方法不适用于带有Visual C ++ 2015的Win32。什么的工作对我来说是使用_set_se_translator()中发现的eh.h。它是这样的:

步骤1)确保在“项目属性” /“ C ++” /“代码生成” /“启用C ++异常”中启用带有SEH异常(/ EHa)的“,如Volodymyr Frytskyy的回答中所述

步骤2)调用_set_se_translator(),传入新异常转换器的函数指针(或lambda)。之所以称为翻译器,是因为它基本上只接受低级别的异常并将其重新引发为更容易捕获的东西,例如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()方法的简单示例,对我
有用

8

这种情况取决于实现,因此将需要特定于供应商的机制才能进行捕获。对于Microsoft,这将涉及SEH,而* nix将涉及一个信号

通常,尽管捕获访问冲突异常是一个非常糟糕的主意。几乎没有办法从AV异常中恢复,尝试这样做将导致程序中更难发现错误。


1
因此,您的建议是了解导致AV异常的原因,不是吗?
艾哈迈德·赛义德

4
绝对。AV代表代码中的错误,捕获异常只会隐藏问题。
JaredPar

1
为了明确起见,C ++标准在未定义,未指定和实现定义之间进行了区分。定义的实现意味着实现必须指定发生了什么。问题中的代码是未定义的,这意味着一切都可能发生,并且每次都不同。
KeithB

15
捕获访问冲突并不是一个坏主意-对用户体验有利。但是,在这种情况下,我唯一有意义的事情是-使用Bug Reporting GUI生成另一个进程,并尝试创建当前进程转储。产生一个进程总是成功的操作。然后,我执行TerminateProcess()来自杀。
ПетърПетров

12
捕获异常并静默忽略它是一个坏主意。尽可能捕获异常并记录有关应用程序状态的信息以用于诊断目的是一个非常好的主意。我曾经为后端图形库编写了一个UI,该UI需要进行一些调试。每当它崩溃时,人们都会来找我,因为他们知道我编写了UI。我在后端放置了一个sig陷阱,并弹出一个警报,通知用户该库已崩溃。人们开始去找图书馆的作者。
肯特2014年

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编译器被视为非法指令或访问冲突。有趣。
David Thornley,2009年

3

不是异常处理机制,但是您可以使用C提供的signal()机制。

> man signal

     11    SIGSEGV      create core image    segmentation violation

写入NULL指针可能会导致SIGSEGV信号


@maidamaisignal()是posix标准的一部分。Windows实现posix标准(Linux和unix也是如此)
Martin York

-1

那样的违规意味着代码中有严重的错误,并且是不可靠的。我可以看到程序可能希望以一种希望不会覆盖以前的数据的方式来尝试保存用户的数据,以希望用户的数据尚未损坏,但是根据定义,没有标准方法处理未定义的行为。


6
从访问冲突中恢复可能是可能的。除非您不懂事并保留汇编级指令指针,否则永远不可能从EIP跳变中恢复。但是,捕获访问冲突非常适合生成另一个报告错误的GUI功能的过程。
ПетърПетров
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.