액세스 위반 예외 포착?


89

int *ptr;
*ptr = 1000;

Microsoft 특정을 사용하지 않고 표준 C ++를 사용하여 메모리 액세스 위반 예외를 포착 할 수 있습니까?

답변:


43

아니. C ++는 성능 저하를 초래할 수있는 나쁜 일을 할 때 예외를 발생시키지 않습니다. 액세스 위반 또는 0으로 나누기 오류와 같은 것은 포착 할 수있는 언어 수준의 것보다 "기계"예외에 가깝습니다.


나는 그것이 HW 예외라는 것을 알고 있지만 Microsoft 특정 키워드가 이것을 처리합니까 (__try __except)?
Ahmed Said

2
@Ahmed : 예,하지만 사용하면 '불가능한'일이 발생할 수 있습니다. 예를 들어, 코드의 AV 라인 의 일부 명령문 은 이미 실행되었거나 AV 이전의 명령문이 실행되지 않았을 수 있습니다.
Aaron

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 ++에서는 작동하지만 "디버그"빌드에서만 작동합니다. 흥미로운 솔루션을 위해 여전히 찬성 투표 중입니다. 신호 핸들러가 호출되지만 예외가 발생하지 않습니다.
Natalie Adams

2
그것은 휴대용으로 작동하지 않습니다. 시그널 핸들러가 호출 될 때 스택 프레임과 레지스터 병합은 일반 함수 스택 프레임과 동일하지 않습니다 (일부 시스템에서는 동일한 스택을 사용하지 않을 수도 있음). 할 수있는 최선의 방법은 신호 처리기가 활성화되었음을 나타내는 플래그를 설정하는 것입니다. 그런 다음 코드에서 해당 플래그를 테스트하고 던집니다.
Martin York

2
이로 인해 정의되지 않은 동작이 발생할 가능성이 높습니다. 이것이 POSIX에서 작동하려면 대체 신호 스택 ( sigaltstack)이 설치 되어서는 안되며 (C ++ 예외 해제 구현이 허용하지 않는 한) 해제 메커니즘 자체를 처리하는 모든 런타임 함수는 신호에 안전해야합니다.
minmaxavg 2017

1
기본 핸들러를 신호 (이 경우 SIGSEGV)로 signal(SIGSEGV, SIG_DFL);
되돌리려

67

try- > catch (...) 블록을 사용하여 Visual Studio 에서 모든 종류의 예외 (0으로 나누기, 액세스 위반 등)를 포착하는 매우 쉬운 방법이 있습니다. 사소한 프로젝트 설정 조정만으로도 충분합니다. 프로젝트 설정에서 / EHa 옵션을 활성화하기 만하면됩니다. 참조 프로젝트 속성 -> C / C ++ -> 코드 생성은 -> 수정은 "예 SEH 예외로"에 C ++ 예외를 활성화합니다 . 그게 다야!

자세한 내용은 http://msdn.microsoft.com/en-us/library/1deeycx5(v=vs.80).aspx를 참조하십시오.


Visual Studio .NET 2003에는 이러한 설정 값이 없으며 "아니오"와 "예 (/ EHsc)"만 있습니다. 이 설정을 활성화하는 데 필요한 Visual Studio의 최소 버전을 명확히 할 수 있습니까?
izogfif

링크는 "Visual Studio 2005에서"지정 표시
드류 델라 노

2
gcc 또는 MinGW를 사용하면 어떨까요?
user1024

10

적어도 나에게는 signal(SIGSEGV ...)다른 답변에서 언급 한 접근 방식 이 Visual C ++ 2015의 Win32에서 작동하지 않았습니다 . 무엇을 했던 나에 대한 작업은 사용하는 것이었다 _set_se_translator()에서 발견 eh.h. 다음과 같이 작동합니다.

1 단계 ) 활성화해야합니다 SEH 예외 (/ EHA)와 예 에서 C ++ 예외를 활성화 프로젝트 속성 / C ++ / 코드 생성 / 에 의해이 질문에 대해 언급 한 바와 같이, 볼로디미르 Frytskyy .

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

1
명확히하기 위해 C ++ 표준은 정의되지 않음, 지정되지 않음 및 정의 된 구현을 구분합니다. 구현 정의는 구현시 발생하는 사항을 지정해야 함을 의미합니다. 질문의 코드는 정의되지 않았기 때문에 모든 일이 발생할 수 있으며 매번 다를 수 있습니다.
KeithB

15
액세스 위반을 포착하는 것은 나쁜 생각이 아니라 사용자 경험에 좋습니다. 그러나이 경우에 내가하는 유일한 의미있는 일은 버그보고 GUI로 다른 프로세스를 생성하고 현재 프로세스 덤프를 생성하는 것입니다. 프로세스 생성은 항상 성공적인 작업입니다. 그런 다음 TerminateProcess ()를 수행하여 자체 죽입니다.
Петър Петров 2011-09-11

12
예외를 포착하고 조용히 무시하는 것은 나쁜 생각입니다. 가능한 경우 예외를 포착하고 진단 목적으로 응용 프로그램의 상태에 대한 정보를 기록하는 것이 좋습니다. 디버깅이 필요한 백엔드 그래픽 라이브러리 용 UI를 작성한 적이 있습니다. 충돌 할 때마다 사람들은 내가 UI를 작성했다는 것을 알았 기 때문에 내게 왔습니다. 사용자에게 라이브러리가 충돌했음을 알리는 경고를 표시하는 백엔드 주변에 시그 트랩을 배치했습니다. 사람들은 도서관의 저자에게 가기 시작했습니다.
켄트

8

언급했듯이 Windows 플랫폼에서이 작업을 수행하는 비 Microsoft / 컴파일러 공급 업체 방법은 없습니다. 그러나 오류보고를위한 일반적인 try {} catch (예외 예) {} 방식으로 이러한 유형의 예외를 포착하고 앱을 더 우아하게 종료하는 것은 분명히 유용합니다 (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

3

예외 처리 메커니즘은 아니지만 C에서 제공하는 signal () 메커니즘을 사용할 수 있습니다.

> man signal

     11    SIGSEGV      create core image    segmentation violation

NULL 포인터에 쓰는 것은 아마도 SIGSEGV 신호를 유발할 것입니다.


@maidamai signal()는 posix 표준의 일부입니다. 윈도우 구현 POSIX 표준을 (리눅스와 유닉스처럼)
마틴 뉴욕

-1

이와 같은 위반은 코드에 심각한 문제가 있으며 신뢰할 수 없음을 의미합니다. 프로그램이 사용자의 데이터가 이미 손상되지 않았 으면하는 바람에 이전 데이터를 덮어 쓰지 않기를 바라는 방식으로 사용자의 데이터를 저장하려고 할 수 있지만 정의상 표준 방법은 없습니다. 정의되지 않은 행동을 다루는 것.


6
액세스 위반에서 복구가 가능할 수 있습니다. 어리 석고 어셈블리 수준의 지침 포인터를 유지하지 않는 한 EIP 점프 변동으로부터 복구하는 것은 불가능합니다. 그러나 액세스 위반을 포착하는 것은 버그보고 GUI 기능을위한 다른 프로세스를 생성하는 데 유용합니다.
Петър Петров 2011-09-11
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.