C에서 예외를 던지는 방법은 무엇입니까?


102

나는 이것을 구글에 입력했지만 C ++에서만 howtos를 찾았습니다.

C로하는 방법?


6
C는 예외 처리를 지원하지 않습니다. C에서 예외를 발생 시키려면 Win32의 구조적 예외 처리와 같은 특정 플랫폼을 사용해야합니다. 그러나 이에 대한 도움을 제공하려면 관심있는 플랫폼을 알아야합니다.
Jerry Coffin

18
... 그리고 Win32 구조적 예외 처리를 사용하지 마십시오.
Brian R. Bondy

4
이론적으로는 setjmp ()와 longjmp ()를 사용하면 효과가 있지만 그럴만 한 가치가 없다고 생각합니다.
Joseph Quinsey

답변:


78

C에는 예외가 없습니다. C에서 오류는 함수의 반환 된 값, 프로세스의 종료 값, 프로세스에 대한 신호 ( 프로그램 오류 신호 (GNU libc) ) 또는 CPU 하드웨어 중단 (또는 기타 알림 오류가있는 경우 CPU에서 오류) ( 프로세서가 0으로 나누는 경우를 처리하는 방법 ).

예외는 C ++ 및 기타 언어로 정의됩니다. C ++의 예외 처리는 C ++ 표준 "S.15 예외 처리"에 지정되어 있으며 C 표준에는 해당 섹션이 없습니다.


4
그래서 C에서는 예외가 없다는 것이 보장됩니다. 어떻게?
httpinterpret

62
@httpinterpret : C에서는 템플릿이 없거나 리플렉션이 없거나 유니콘이 없음을 "보장"하는 것과 같은 방식으로 예외가 없음을 "보장"합니다. 언어 사양은 단순히 그런 것을 정의하지 않습니다. 반환하지 않고 함수를 종료하는 데 사용할 수있는 setjmp / longjmp가 있습니다. 따라서 프로그램은 원하는 경우 자체 예외와 유사한 메커니즘을 구축하거나 C 구현이 표준에 대한 확장을 정의 할 수 있습니다.
Steve Jessop

2
와 프로그램 mainA의 .c자주 C로 작성 기능에 의존하는 파일은 일부 C ++를 포함 할 수 있으므로 예외가 발생하고 프로그램에서 잡은 있지만, C 코드 부분이 그 예외 던지기를 제외하고가는이 모든 무지 남아 잡는 수 C ++ 라이브러리에 있습니다. C는 throw예외 자체를 던질 필요가 있도록 호출 된 함수가 위험 할 수 없기 때문에 사용 됩니다. 하지만 예외를 throw / catch하는 컴파일러 / 라이브러리 / 타겟 특정 방법이있을 수 있습니다. 그러나 클래스 인스턴스를 던지는 것은 나름대로 문제가 있습니다.
nategoose

61
@Steve : 유니콘이있는 언어를 찾으면 알려주세요. 저는 수년 동안 그런 일을 기다려 왔습니다.
Brian R. Bondy

5
@ BrianR.Bondy 여기 에 유니콘이있는 언어가 있습니다 (C에서 보장하는 것과 동일한 방식으로 보장합니다)
Tofandel 2014 년

38

C에서는 에 정의 된 setjmp()longjmp()함수 의 조합을 사용할 수 있습니다 setjmp.h. Wikipedia의 예

#include <stdio.h>
#include <setjmp.h>

static jmp_buf buf;

void second(void) {
    printf("second\n");         // prints
    longjmp(buf,1);             // jumps back to where setjmp 
                                //   was called - making setjmp now return 1
}

void first(void) {
    second();
    printf("first\n");          // does not print
}

int main() {   
    if ( ! setjmp(buf) ) {
        first();                // when executed, setjmp returns 0
    } else {                    // when longjmp jumps back, setjmp returns 1
        printf("main");         // prints
    }

    return 0;
}

참고 : 실제로 C ++ (로컬 객체의 소멸자가 호출되지 않음)로 작동하므로 사용 하지 않는 것이 좋으며 무슨 일이 일어나고 있는지 이해하기가 정말 어렵습니다. 대신 어떤 종류의 오류를 반환하십시오.


예외가 사용될 때 파괴가 필요한 객체가없는 경우에도 setjump / longjump가 C ++ 프로그램에서 제대로 작동하지 않는 것을 보았습니다. C 프로그램에서는 거의 사용하지 않습니다.
nategoose

이것은 setjmp를 사용한 흥미로운 접근 방식이었습니다. on-time.com/ddj0011.htm 하지만 예, 기본적으로 스택을 풀지 않고 대역 외 코드 실행을 원한다면 직접 발명해야합니다.
Dwayne Robinson

질문이 C에 관한 것이고 C ++에 예외가있을 때 C ++에서 사용하는 것에 대한 경고가 왜 그런지 잘 모르겠습니다. 어떤 경우에, 영업 이익은 당신의 다리를 촬영에서 setjmp는 / longjmp를 구현을 유지하기 위해 항상 염두에 두어야 것을 알고하는 것이 중요합니다 당신 먼저 (그것을 설정하는) 오류 핸들러를 방문하고, 그 오류 처리기 필요 두 번 반환해야합니다.

1
그건 그렇고, 예외 처리는 C11에서 끝날 것이라고 기대했던 것입니다. 일반적인 믿음에도 불구하고 OOP가 제대로 작동 할 필요 는 없으며 C는 if(). 나는 그 안에있는 위험을 볼 수 없습니다. 여기 다른 사람이 있습니까?

@ user4229245 나는 8 비트 바이트와 같은 현상 유지 기본 사항조차도없는 베어 메탈 및 기계 프로그래밍에 대해 c 관련성을 유지하기 위해 "당신을 위해 수행 된"모든 것이 가능한 한 c 언어 사양에서 제외된다는 인상을 받고 있습니다. 보편적 인 것이 아니라 내 생각뿐입니다. 저는 C 사양 작성자가
아닙니다

21

Plain old C는 실제로 기본적으로 예외를 지원하지 않습니다.

다음과 같은 대체 오류 처리 전략을 사용할 수 있습니다.

  • 오류 코드 반환
  • 변수 또는 함수 반환 FALSE및 사용 last_error.

http://en.wikibooks.org/wiki/C_Programming/Error_handling을 참조하십시오 .


또한 Windows API에는 오류를 처리하는 동일한 방법이 있습니다. 예를 들어 GetLastError()Windows API에서.
BattleTested

20

C 에는 내장 된 예외 메커니즘 이 없습니다 . 예외와 그 의미 를 시뮬레이션 해야합니다 . 이것은 일반적으로 setjmplongjmp .

주변에 꽤 많은 라이브러리가 있으며 또 다른 라이브러리를 구현하고 있습니다. 이것은 exceptions4c 라고합니다 . 휴대 가능하고 무료입니다. 그것을 살펴보고 다른 대안 과 비교하여 가장 적합한 것을 확인할 수 있습니다.


코드 예제를 읽었습니다. 구조가있는 비슷한 프로젝트를 시작했지만 문자열 대신 uuid를 사용하여 각 "예외"를 식별합니다. 귀하의 프로젝트는 매우 유망 해 보입니다.
umlcat


귀하 (또는 다른 사람)가 다른 예외 패키지 간의 비교에 대해 알고 있습니까?
Marcel Waldvogel

11

C는 C ++ 예외를 던질 수 있지만 어쨌든 기계 코드입니다. 예를 들어, bar.c에서

// begin bar.c
#include <stdlib.h>
#include <stdint.h>
extern void *__cxa_allocate_exception(size_t thrown_size);
extern void __cxa_throw (void *thrown_exception, void* *tinfo, void (*dest) (void *) );
extern void * _ZTIl; // typeinfo of long
int bar1()
{
   int64_t * p = (int64_t*)__cxa_allocate_exception(8);
   *p = 1976;
   __cxa_throw(p,&_ZTIl,0);
  return 10;
}
// end bar.c

a.cc에서

#include <stdint.h>
#include <cstdio>
extern "C" int bar1();
void foo()
{
  try{
    bar1();
  }catch(int64_t x){
    printf("good %ld",x);
  }
}
int main(int argc, char *argv[])
{
  foo();
  return 0;
}

그것을 컴파일하기 위해

gcc -o bar.o -c bar.c && g++ a.cc bar.o && ./a.out

산출

good 1976

http://mentorembedded.github.io/cxx-abi/abi-eh.html 에 대한 자세한 정보가 있습니다.__cxa_throw 있습니다.

나는 그것이 이식 가능한지 아닌지 확실하지 않으며 Linux에서 'gcc-4.8.2'로 테스트합니다.


2
도대체 "_ZTIl"??? 어디에서나 그것에 대한 참조를 찾을 수 없으며 C 코드가 std :: type_info에 액세스 할 수있게하는 방법은 완전한 미스터리입니다. 제발 ... 도와주세요!
AreusAstarte

1
_ZTII의미 typeinfo for long, 예 echo '_ZTIl' | c++filt . g ++ 맹 글링 방식입니다.
wcy

그리고 좋은 측정을 위해 C ++를 가능한 한 많이 피하기 위해 catch 블록에서 ac 기호를 호출해야합니다. : P (우수한 실제 예제를 공유해 주셔서 감사합니다!)
ThorSummoner

8

이 질문은 매우 오래되었지만 방금 우연히 발견하여 0으로 나누거나 널 포인터를 역 참조하는 기술을 공유 할 것이라고 생각했습니다.

문제는 단순히 "어떻게 던지는가"이지, 잡는 방법이나 특정 유형의 예외를 던지는 방법이 아닙니다. 나는 C ++에서 잡히기 위해 C에서 예외를 트리거해야하는 오래 전에 상황이 있었다. 특히, "순수한 가상 함수 호출"오류에 대한보고가 가끔 있었으며 C 런타임의 _purecall 함수가 무언가를 던지도록 설득해야했습니다. 그래서 우리는 0으로 나눈 우리 자신의 _purecall 함수를 추가했습니다. 그리고 boom, 우리는 C ++에서 잡을 수있는 예외를 얻었고, 어떤 일이 잘못되었는지보기 위해 스택 재미를 사용했습니다.


@AreusAstarte 누군가가 정말로 0으로 C 나누기를 표시해야하는 경우를 대비하여 :int div_by_nil(int x) { return x/0; }

7

MSVC를 사용하는 Win 에는 __try ... __except ...정말 끔찍하며 피할 수 있다면 사용하고 싶지 않습니다. 예외가 없다고 말하는 것이 좋습니다.


1
실제로 C ++ 예외는 SEH의 상단에도 내장되어 있습니다.
Calmarius

@Calmarius SEH 는 무엇입니까 ?
Marcel Waldvogel

1
@MarcelWaldvogel 구조적 예외 처리 (Windows의 CPU 예외 처리 방식).
Calmarius


3

여러 스레드에서 언급했듯이이를 수행하는 "표준"방법은 setjmp / longjmp를 사용하는 것입니다. https://github.com/psevon/exceptions-and-raii-in-c에 또 다른 솔루션을 게시했습니다. 이것은 할당 된 리소스의 자동 정리에 의존하는 유일한 솔루션입니다. 고유하고 공유 된 스마트 포인터를 구현하고 중간 함수가 예외를 포착하지 않고 통과하도록 허용하고 로컬에서 할당 된 리소스를 적절하게 정리할 수 있습니다.


2

C는 예외를 지원하지 않습니다. Visual Studio 또는 G ++를 사용하여 C 코드를 C ++로 컴파일하고있는 그대로 컴파일되는지 확인할 수 있습니다. 대부분의 C 응용 프로그램은 큰 변경없이 C ++로 컴파일되므로 try ... catch 구문을 사용할 수 있습니다.


0

해피 경로 디자인 패턴 (예 : 임베디드 장치 용)으로 코드를 작성하면 "goto"연산자를 사용하여 예외 오류 처리 (일명 deffering 또는 최종 에뮬레이션)를 시뮬레이션 할 수 있습니다.

int process(int port)
{
    int rc;
    int fd1;
    int fd2;

    fd1 = open("/dev/...", ...);
    if (fd1 == -1) {
      rc = -1;
      goto out;
    }

    fd2 = open("/dev/...", ...);
    if (fd2 == -1) {
      rc = -1;
      goto out;
    }

    // Do some with fd1 and fd2 for example write(f2, read(fd1))

    rc = 0;

   out:

    //if (rc != 0) {
        (void)close(fd1);
        (void)close(fd2);
    //}

    return rc;
}

실제로 예외 처리기는 아니지만 기능 종료시 오류를 처리하는 방법을 사용합니다.

추신 : 동일하거나 더 깊은 범위에서만 goto를 사용하고 변수 선언을 점프하지 마십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.