try...catch C에서

Muhammad Husnain 2023년10월12일
  1. C의 Try-Catch
  2. C의 Try-CatchFinally 추가
try...catch C에서

Try-Catch 메커니즘은 Python, C++ 및 JavaScript와 같은 많은 프로그래밍 언어에서 일반적입니다. 일반적인 구조는 아래와 같습니다.

try {
  /*
  Insert some lines of code that will probably give you errors
  */
} catch {
  /*
  Write some code to handle the errors you're getting.
  */
}

각 명령문을 테스트하지 않고도 코드를 작성할 수 있습니다. try 블록에서 실행 중인 프로그램이 예외에 도달하면 예외가 catch 블록으로 전달됩니다.

예외가 일부 예외 유형과 일치하면 catch 블록 내의 코드가 실행됩니다. 그렇지 않으면 예외가 try 블록으로 다시 전달됩니다.

C의 Try-Catch

C는 예외 처리를 지원하지 않습니다. 적어도 내장 메커니즘이 없습니다.

이 가이드는 C에서 try-catch 기능을 제공하는 가능한 솔루션을 보여줍니다. 솔루션이 반드시 완전하지는 않다는 점에 유의해야 합니다.

예외 처리 시스템은 스택이 순회되고 C에 가비지 수집기가 없을 때 메모리를 해제하는 메커니즘 없이는 완전하고 안전하지 않습니다. 또한 메모리를 확보하기 위해 컨텍스트 관리자를 포함해야 할 수도 있습니다.

이 솔루션은 완전하고 광범위한 try-catch 메커니즘을 제공하지 않습니다. 이 개념은 기술적으로 몇 가지 예외를 처리하는 데 사용할 수 있습니다.

우리는 코드를 업데이트하면서 솔루션을 점진적으로 구축할 것입니다. 우리는 setjmp.h 헤더 파일에서 얻을 수 있는 longjmpsetjmp라는 C에서 제공하는 두 가지 기능을 사용할 것입니다.

두 함수의 정의를 자세히 살펴보겠습니다.

int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);

setjmpjmp_buf 유형의 변수를 사용합니다. 이 함수를 직접 호출하면 ‘0’을 반환한다.

longjmp는 두 개의 변수를 취하며, longjmp가 동일한 jmp_buf 변수로 호출될 때 setjmp 함수는 longjmp(val)의 두 번째 인수와 동일한 값을 반환합니다.

여기서 env 변수는 본질적으로 “호출 환경"이며, 레지스터 상태와 함수 호출이 수행될 때 코드의 위치를 ​​나타냅니다. longjmp가 호출되면 “호출 환경"의 상태가 프로세서에 복사되고 longjmpval 인수에 저장된 값이 반환됩니다.

간단한 Try-Catch 블록의 경우 Try 문을 if 문에 매핑하고 Catch 문은 조건문에 대한 else가 됩니다. 여기에서 setjmp가 다른 값을 반환할 수 있다는 사실을 지능적으로 사용할 수 있습니다.

함수가 0을 반환하면 실행된 유일한 코드 조각이 TRY 블록의 코드라는 것을 알 수 있습니다. 함수가 다른 것을 반환하면 시작할 때와 동일한 상태로 CATCH 블록으로 이동해야 합니다.

예외를 THROW할 때 longjmp 함수를 호출할 수 있습니다.

아래 코드에서 볼 수 있듯이 TRY 블록도 닫아야 합니다. do-while 블록의 닫는 부분을 제공하는 ENDTRY 함수를 만듭니다.

이것은 또한 동일한 블록 내에서 여러 TRY 문을 만드는 데 도움이 됩니다. buf_state 변수를 재사용하므로 중첩될 수 없습니다.

이 구현의 예는 다음과 같습니다.

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

#define TRY            \
  do {                 \
    jmp_buf buf_state; \
    if (!setjmp(buf_state)) {
#define CATCH \
  }           \
  else {
#define ENDTRY \
  }            \
  }            \
  while (0)
#define THROW longjmp(buf_state, 1)

int main() {
  TRY {
    printf("Testing Try statement \n");
    THROW;
    printf(
        "Statement should not appear, as the THROW block has already thrown "
        "the exception \n");
  }
  CATCH { printf("Got Exception \n"); }
  ENDTRY;

  return 0;
}

출력:

Testing Try statement
Got Exception

실제 시스템의 경우 이것으로 충분하지 않습니다. 다양한 유형의 예외가 있어야 합니다.

위의 예는 한 가지 유형의 예외만 지원합니다. 다시 한 번, setjmp의 다른 반환 값을 사용할 수 있습니다.

if-else를 사용하는 대신 switch-case로 전환합니다.

디자인은 다음과 같습니다. TRY 문은 switch 문을 사용하고 CATCH는 예외 유형을 나타내는 매개변수가 있는 매크로입니다. 각 CATCH 문의 조건은 break를 사용하여 이전 case를 닫아야 한다는 것입니다.

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

#define TRY                      \
  do {                           \
    jmp_buf buf_state;           \
    switch (setjmp(buf_state)) { \
      case 0:
#define CATCH(x) \
  break;         \
  case x:
#define ENDTRY \
  }            \
  }            \
  while (0)
#define THROW(x) longjmp(buf_state, x)

#define EXCEPTION1 (1)
#define EXCEPTION2 (2)
#define EXCEPTION3 (3)

int main() {
  TRY {
    printf("Inside Try statement \n");
    THROW(EXCEPTION2);
    printf("This does not appear as exception has already been called \n");
  }
  CATCH(EXCEPTION1) { printf("Exception 1 called \n"); }
  CATCH(EXCEPTION2) { printf("Exception 2 called \n"); }
  CATCH(EXCEPTION3) { printf("Exception 3 called \n"); }
  ENDTRY;

  return 0;
}

출력:

Inside Try statement
Exception 2 called

C의 Try-CatchFinally 추가

완전한 기능의 Try-Catch 구현을 위해 FINALLY 블록을 추가해야 합니다. finally 블록은 일반적으로 trycatch 블록이 완료된 후 실행됩니다.

예외가 발생했는지 여부에 관계없이 실행됩니다.

이것을 어떻게 구현할까요? 핵심 아이디어는 FINALLY 블록을 구현하기 위해 switch 케이스의 default 케이스를 사용하는 것입니다.

그러나 switch-case는 일반적인 경우에 예외가 호출된 경우 default 사례를 실행하지 않습니다.

Duff’s Device와 유사한 메커니즘을 사용합니다. 우리는 본질적으로 switch-case 문을 do-while 문과 엮을 것입니다.

그에 대한 논리는 이렇습니다.

switch (an expression) {
  case 0:
    while (1) {
      // code for case 0
      break;
      case 1:
        // code for case 1
        break;
    }
  default:
    // code for default case
}

우리는 C에서 가장 논란의 여지가 있는 기능 중 하나를 사용합니다. 각 케이스 레이블 앞에 자동으로 끊어지지 않는 스위치입니다. 여기에서 while 문은 break가 호출될 때 switch-case 안에 중첩됩니다. while 루프를 종료하고 케이스를 계속 탐색합니다.

우리 코드의 맥락에서(아래 그림 참조) switch 케이스는 모두 예외이고 default 케이스는 FINALLY 블록에 있습니다. 당연히 예외 코드가 이미 실행되었기 때문에 default 케이스에 해당합니다.

이것은 아래 코드에 나와 있습니다.

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

#define TRY                      \
  do {                           \
    jmp_buf buf_state;           \
    switch (setjmp(buf_state)) { \
      case 0:                    \
        while (1) {
#define CATCH(x) \
  break;         \
  case x:
#define ENDTRY \
  }            \
  }            \
  while (0)
#define THROW(x) longjmp(buf_state, x)
#define FINALLY \
  break;        \
  }             \
  default:
#define EXCEPTION1 (1)
#define EXCEPTION2 (2)
#define EXCEPTION3 (3)

int main() {
  TRY {
    printf("Inside Try statement \n");
    THROW(EXCEPTION2);
    printf("This does not appear as exception has already been called \n");
  }
  CATCH(EXCEPTION1) { printf("Exception 1 called \n"); }
  CATCH(EXCEPTION2) { printf("Exception 2 called \n"); }
  CATCH(EXCEPTION3) { printf("Exception 3 called \n"); }
  FINALLY { printf("This will always be called! \n"); }
  ENDTRY;

  return 0;
}

출력:

Inside Try statement
Exception 2 called
This will always be called!

이것으로 C에서 try-catch 시스템을 만들기 위한 가이드를 마칩니다. 물론 여기에 가능한 메모리 문제와 몇 가지 제한 사항(예: 중첩 try-catch 시스템에 대한 지원 부족)이 있지만 이것은 C에서 기능적 try-catch 구현

Muhammad Husnain avatar Muhammad Husnain avatar

Husnain is a professional Software Engineer and a researcher who loves to learn, build, write, and teach. Having worked various jobs in the IT industry, he especially enjoys finding ways to express complex ideas in simple ways through his content. In his free time, Husnain unwinds by thinking about tech fiction to solve problems around him.

LinkedIn

관련 문장 - C Exception