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 에는 내장 된 예외 메커니즘 이 없습니다 . 예외와 그 의미 를 시뮬레이션 해야합니다 . 이것은 일반적으로 setjmp및longjmp .

주변에 꽤 많은 라이브러리가 있으며 또 다른 라이브러리를 구현하고 있습니다. 이것은 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.