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
헤더 파일에서 얻을 수 있는 longjmp
와 setjmp
라는 C에서 제공하는 두 가지 기능을 사용할 것입니다.
두 함수의 정의를 자세히 살펴보겠습니다.
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);
setjmp
는 jmp_buf
유형의 변수를 사용합니다. 이 함수를 직접 호출하면 ‘0’을 반환한다.
longjmp
는 두 개의 변수를 취하며, longjmp
가 동일한 jmp_buf
변수로 호출될 때 setjmp
함수는 longjmp
(val
)의 두 번째 인수와 동일한 값을 반환합니다.
여기서 env
변수는 본질적으로 “호출 환경"이며, 레지스터 상태와 함수 호출이 수행될 때 코드의 위치를 나타냅니다. longjmp
가 호출되면 “호출 환경"의 상태가 프로세서에 복사되고 longjmp
의 val
인수에 저장된 값이 반환됩니다.
간단한 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-Catch
에 Finally
추가
완전한 기능의 Try-Catch
구현을 위해 FINALLY
블록을 추가해야 합니다. finally
블록은 일반적으로 try
및 catch
블록이 완료된 후 실행됩니다.
예외가 발생했는지 여부에 관계없이 실행됩니다.
이것을 어떻게 구현할까요? 핵심 아이디어는 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
구현
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