C에서 catch 문 시도


101

나는 오늘 다른 언어에 존재하는 try / catch 블록에 대해 생각하고있었습니다. 잠시 동안 검색했지만 결과가 없습니다. 내가 아는 바에 따르면 C에서 try / catch와 같은 것은 없습니다. 그러나 그것들을 "시뮬레이션"하는 방법이 있습니까?
물론, assert와 다른 트릭이 있지만 try / catch와 같은 것은 없습니다. 감사합니다


3
예외와 같은 메커니즘은 스택이 풀릴 때 자동으로 리소스를 해제하는 메커니즘 없이는 일반적으로 유용하지 않습니다. C ++는 RAII를 사용합니다. Java, C #, Python 등은 가비지 수집기를 사용합니다. (가비지 수집기는 메모리 만 해제합니다. 다른 유형의 리소스를 자동으로 해제하기 위해 종료 자 또는 컨텍스트 관리자와 같은 것도 추가합니다 ...)
jamesdlin

@jamesdlin, 왜 우리는 C로 RAII를 할 수 없습니까?
Pacerier

1
@Pacerier RAII는 객체가 소멸 될 때 자동으로 함수를 호출해야합니다 (즉, 소멸자). C에서 어떻게 제안합니까?
jamesdlin

답변:


90

C 자체는 예외를 지원하지 않지만 setjmplongjmp호출 을 사용하여 어느 정도 시뮬레이션 할 수 있습니다 .

static jmp_buf s_jumpBuffer;

void Example() { 
  if (setjmp(s_jumpBuffer)) {
    // The longjmp was executed and returned control here
    printf("Exception happened here\n");
  } else {
    // Normal code execution starts here
    Test();
  }
}

void Test() {
  // Rough equivalent of `throw`
  longjmp(s_jumpBuffer, 42);
}

이 웹 사이트에는 setjmp및 예외를 시뮬레이션하는 방법에 대한 멋진 자습서가 있습니다.longjmp


1
멋진 솔루션! 이 솔루션은 교차합니까? MSVC2012에서는 작동했지만 MacOSX Clang 컴파일러에서는 작동하지 않았습니다.
mannysz 2016 년

1
단서 : try catch 절을 사용하면 예외를 잡을 수 있다고 생각했습니다 (예 : 0으로 나누기). 이 함수는 자신이 던진 예외를 포착하는 것만 허용하는 것 같습니다. longjmp를 호출하면 실제 예외가 발생하지 않습니까? 이 코드를 사용하여 try{ x = 7 / 0; } catch(divideByZeroException) {print('divided by zero')}; 제대로 작동 하지 않는 것과 같은 작업 을 수행 하면?
Sam

0으로 Devide는 C ++에서 예외도 아니므로이를 처리하려면 제수가 0이 아닌지 확인하고 처리하거나 0으로 devide 수식을 실행할 때 throw되는 SIGFPE를 처리해야합니다.
James

25

유사한 오류 처리 상황을 위해 C에서 goto 를 사용 합니다.
이것은 C에서 얻을 수있는 예외와 가장 비슷합니다.


3
@JensGustedt 이것은 goto가 현재 매우 자주 사용되는 것이며 의미있는 예입니다 (setjmp / ljmp가 더 나은 대안이지만 일반적으로 label + goto가 더 많이 사용됨).
Tomas Pruzina 2013

1
@AoeAoe, 아마도 goto오류 처리에 더 많이 사용되지만 어떻게 될까요? 문제는 오류 처리에 관한 것이 아니라 try / catch 등가물에 관한 것입니다. goto동일한 기능으로 제한되기 때문에 try / catch와 동등하지 않습니다.
Jens Gustedt 2013

@JensGustedt 나는 goto와 그것을 사용하는 사람들에 대한 증오 / 두려움에 다소 반응했습니다 (제 선생님은 대학에서 goto 사용에 대한 무서운 이야기를 들었습니다). [OT] goto에 대해 정말, 정말 위험하고 '흐릿한'유일한 것은 '거꾸로 이동'하는 것입니다.하지만 저는 Linux VFS에서 그것을 보았습니다 (git blame guy는 이것이 성능에 매우 유익하다고 맹세했습니다).
Tomas Pruzina 2013

현대적이고 광범위하게 수용되는 피어 리뷰 소스에서 사용되는 try / catch 메커니즘으로의 합법적 인 사용에 대해서는 systemctl 소스 를 참조하십시오 goto. 검색 goto동등한 "던져"를, 그리고 finish는 "캐치"에 대한 상응.
Stewart

13

좋아, 나는 이것에 대답하는 것을 거부 할 수 없었다. 먼저 C에 대한 외래 개념이므로 C로 시뮬레이션하는 것이 좋지 않다고 생각합니다.

제한된 버전의 C ++ try / throw / catch를 사용하기 위해 전처리 기와 로컬 스택 변수를 남용 할 수 있습니다 .

버전 1 (로컬 범위 발생)

#include <stdbool.h>

#define try bool __HadError=false;
#define catch(x) ExitJmp:if(__HadError)
#define throw(x) __HadError=true;goto ExitJmp;

버전 1은 로컬 throw 전용입니다 (함수 범위를 벗어날 수 없음). 코드에서 변수를 선언하는 C99의 기능에 의존합니다 (함수에서 시도가 첫 번째 인 경우 C89에서 작동해야 함).

이 함수는 단지 로컬 var를 만들어 오류가 있는지 알고 goto를 사용하여 catch 블록으로 이동합니다.

예를 들면 :

#include <stdio.h>
#include <stdbool.h>

#define try bool __HadError=false;
#define catch(x) ExitJmp:if(__HadError)
#define throw(x) __HadError=true;goto ExitJmp;

int main(void)
{
    try
    {
        printf("One\n");
        throw();
        printf("Two\n");
    }
    catch(...)
    {
        printf("Error\n");
    }
    return 0;
}

이것은 다음과 같이 작동합니다.

int main(void)
{
    bool HadError=false;
    {
        printf("One\n");
        HadError=true;
        goto ExitJmp;
        printf("Two\n");
    }
ExitJmp:
    if(HadError)
    {
        printf("Error\n");
    }
    return 0;
}

버전 2 (범위 점프)

#include <stdbool.h>
#include <setjmp.h>

jmp_buf *g__ActiveBuf;

#define try jmp_buf __LocalJmpBuff;jmp_buf *__OldActiveBuf=g__ActiveBuf;bool __WasThrown=false;g__ActiveBuf=&__LocalJmpBuff;if(setjmp(__LocalJmpBuff)){__WasThrown=true;}else
#define catch(x) g__ActiveBuf=__OldActiveBuf;if(__WasThrown)
#define throw(x) longjmp(*g__ActiveBuf,1);

버전 2는 훨씬 더 복잡하지만 기본적으로 동일한 방식으로 작동합니다. 현재 함수에서 try 블록으로의 긴 점프를 사용합니다. 그런 다음 try 블록은 if / else를 사용하여 코드 블록을 catch 블록으로 건너 뛰고 로컬 변수가 catch해야하는지 확인합니다.

예제가 다시 확장되었습니다.

jmp_buf *g_ActiveBuf;

int main(void)
{
    jmp_buf LocalJmpBuff;
    jmp_buf *OldActiveBuf=g_ActiveBuf;
    bool WasThrown=false;
    g_ActiveBuf=&LocalJmpBuff;

    if(setjmp(LocalJmpBuff))
    {
        WasThrown=true;
    }
    else
    {
        printf("One\n");
        longjmp(*g_ActiveBuf,1);
        printf("Two\n");
    }
    g_ActiveBuf=OldActiveBuf;
    if(WasThrown)
    {
        printf("Error\n");
    }
    return 0;
}

이것은 전역 포인터를 사용하므로 longjmp ()는 마지막 실행 시도를 알고 있습니다. 우리는 사용하여 자식 기능도 try / catch 블록을 가질 수 있도록 스택을 남용.

이 코드를 사용하면 여러 가지 단점이 있습니다 (하지만 재미있는 정신 운동입니다).

  • 호출되는 해체자가 없기 때문에 할당 된 메모리를 해제하지 않습니다.
  • 한 스코프에 둘 이상의 try / catch를 가질 수 없습니다 (중첩 없음).
  • 실제로 C ++에서와 같은 예외 또는 기타 데이터를 던질 수 없습니다.
  • 스레드로부터 안전하지 않음
  • 다른 프로그래머가 해킹을 인식하지 못하고 C ++ try / catch 블록처럼 사용하려고 시도 할 가능성이 높기 때문에 다른 프로그래머를 실패로 설정하고 있습니다.

좋은 대체 솔루션.
HaseeB Mir

버전 1은 좋은 생각이지만 __HadError 변수는 재설정하거나 범위를 지정해야합니다. 그렇지 않으면 동일한 블록에서 둘 이상의 try-catch를 사용할 수 없습니다. 같은 전역 함수를 사용할 수 bool __ErrorCheck(bool &e){bool _e = e;e=false;return _e;}있습니다. 그러나 지역 변수도 재정의되므로 상황이 조금 어려워집니다.
flamewave000

예, 동일한 기능에서 하나의 try-catch로 제한됩니다. 더 큰 문제는 동일한 기능에서 중복 레이블을 가질 수 없기 때문에 변수는 레이블입니다.
Paul Hutchinson

10

C99에서는 로컬이 아닌 제어 흐름에 setjmp/ longjmp를 사용할 수 있습니다 .

단일 범위 내에서 다중 자원 할당 및 다중 종료가있는 경우 C에 대한 일반 구조화 된 코딩 패턴 은이 예제goto 와 같이 사용합니다 . 이것은 C ++가 내부에서 자동 객체의 소멸자 호출을 구현하는 방법과 유사하며, 이것을 열심히 고수한다면 복잡한 함수에서도 어느 정도의 정리를 허용해야합니다.


5

다른 답변의 일부를 사용하여 간단한 경우를 포함하고 있지만 setjmplongjmp실제 응용 프로그램에서이 정말 문제 두 가지 문제가 있습니다.

  1. try / catch 블록의 중첩. 단일 전역 변수를 사용하면 jmp_buf작동하지 않습니다.
  2. 스레딩. 단일 전역 변수 jmp_buf는이 상황에서 모든 종류의 고통을 유발합니다.

이것에 대한 해결책 jmp_buf은 당신이 갈 때 업데이트 되는 스레드-로컬 스택을 유지하는 것입니다. (나는 이것이 lua가 내부적으로 사용하는 것이라고 생각합니다).

그래서 대신에 (JaredPar의 멋진 답변에서)

static jmp_buf s_jumpBuffer;

void Example() { 
  if (setjmp(s_jumpBuffer)) {
    // The longjmp was executed and returned control here
    printf("Exception happened\n");
  } else {
    // Normal code execution starts here
    Test();
  }
}

void Test() {
  // Rough equivalent of `throw`
  longjump(s_jumpBuffer, 42);
}

다음과 같은 것을 사용합니다.

#define MAX_EXCEPTION_DEPTH 10;
struct exception_state {
  jmp_buf s_jumpBuffer[MAX_EXCEPTION_DEPTH];
  int current_depth;
};

int try_point(struct exception_state * state) {
  if(current_depth==MAX_EXCEPTION_DEPTH) {
     abort();
  }
  int ok = setjmp(state->jumpBuffer[state->current_depth]);
  if(ok) {
    state->current_depth++;
  } else {
    //We've had an exception update the stack.
    state->current_depth--;
  }
  return ok;
}

void throw_exception(struct exception_state * state) {
  longjump(state->current_depth-1,1);
}

void catch_point(struct exception_state * state) {
    state->current_depth--;
}

void end_try_point(struct exception_state * state) {
    state->current_depth--;
}

__thread struct exception_state g_exception_state; 

void Example() { 
  if (try_point(&g_exception_state)) {
    catch_point(&g_exception_state);
    printf("Exception happened\n");
  } else {
    // Normal code execution starts here
    Test();
    end_try_point(&g_exception_state);
  }
}

void Test() {
  // Rough equivalent of `throw`
  throw_exception(g_exception_state);
}

다시 한 번 더 현실적인 버전은 오류 정보를에 저장하는 방법 exception_state, 더 나은 처리 방법을 포함 MAX_EXCEPTION_DEPTH합니다 (버퍼를 늘리기 위해 realloc을 사용하는 등).

면책 조항 : 위의 코드는 테스트없이 작성되었습니다. 순전히 사물을 구성하는 방법에 대한 아이디어를 얻습니다. 다른 시스템과 다른 컴파일러는 스레드 로컬 저장소를 다르게 구현해야합니다. 코드에는 컴파일 오류와 논리 오류가 모두 포함되어있을 수 있으므로 원하는대로 자유롭게 사용할 수 있지만 사용하기 전에 테스트하십시오.)


4

빠른 구글 검색 수익률은 같은 솔루션을 kludgey 다른 언급이 그 사용 setjmp는 / longjmp를. C ++ / Java의 try / catch만큼 간단하고 우아한 것은 없습니다. 나는 Ada의 예외 처리에 다소 부분적입니다.

if 문으로 모든 것을 확인하십시오 :)


4

이 작업 setjmp/longjmp은 C에서 수행 할 수 있습니다 . P99 에는 C11의 새로운 스레드 모델과 일치하는 매우 편리한 도구 세트가 있습니다.


2

이것은 setjmp / longjmp를 사용하는 것보다 성능이 더 뛰어난 C에서 오류 처리를 수행하는 또 다른 방법입니다. 불행히도 MSVC에서는 작동하지 않지만 GCC / Clang 만 사용하는 것이 옵션 인 경우 고려할 수 있습니다. 특히 "label as value"확장을 사용하여 레이블 주소를 가져와 값에 저장하고 무조건 점프 할 수 있습니다. 예를 들어 설명하겠습니다.

GameEngine *CreateGameEngine(GameEngineParams const *params)
{
    /* Declare an error handler variable. This will hold the address
       to jump to if an error occurs to cleanup pending resources.
       Initialize it to the err label which simply returns an
       error value (NULL in this example). The && operator resolves to
       the address of the label err */
    void *eh = &&err;

    /* Try the allocation */
    GameEngine *engine = malloc(sizeof *engine);
    if (!engine)
        goto *eh; /* this is essentially your "throw" */

    /* Now make sure that if we throw from this point on, the memory
       gets deallocated. As a convention you could name the label "undo_"
       followed by the operation to rollback. */
    eh = &&undo_malloc;

    /* Now carry on with the initialization. */
    engine->window = OpenWindow(...);
    if (!engine->window)
        goto *eh;   /* The neat trick about using approach is that you don't
                       need to remember what "undo" label to go to in code.
                       Simply go to *eh. */

    eh = &&undo_window_open;

    /* etc */

    /* Everything went well, just return the device. */
    return device;

    /* After the return, insert your cleanup code in reverse order. */
undo_window_open: CloseWindow(engine->window);
undo_malloc: free(engine);
err: return NULL;
}

원하는 경우 정의에서 공통 코드를 리팩터링하여 자신의 오류 처리 시스템을 효과적으로 구현할 수 있습니다.

/* Put at the beginning of a function that may fail. */
#define declthrows void *_eh = &&err

/* Cleans up resources and returns error result. */
#define throw goto *_eh

/* Sets a new undo checkpoint. */
#define undo(label) _eh = &&undo_##label

/* Throws if [condition] evaluates to false. */
#define check(condition) if (!(condition)) throw

/* Throws if [condition] evaluates to false. Then sets a new undo checkpoint. */
#define checkpoint(label, condition) { check(condition); undo(label); }

그런 다음 예는

GameEngine *CreateGameEngine(GameEngineParams const *params)
{
    declthrows;

    /* Try the allocation */
    GameEngine *engine = malloc(sizeof *engine);
    checkpoint(malloc, engine);

    /* Now carry on with the initialization. */
    engine->window = OpenWindow(...);
    checkpoint(window_open, engine->window);

    /* etc */

    /* Everything went well, just return the device. */
    return device;

    /* After the return, insert your cleanup code in reverse order. */
undo_window_open: CloseWindow(engine->window);
undo_malloc: free(engine);
err: return NULL;
}

2

경고 : 다음은별로 좋지 않지만 제대로 작동합니다.

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    unsigned int  id;
    char         *name;
    char         *msg;
} error;

#define _printerr(e, s, ...) fprintf(stderr, "\033[1m\033[37m" "%s:%d: " "\033[1m\033[31m" e ":" "\033[1m\033[37m" " ‘%s_error’ " "\033[0m" s "\n", __FILE__, __LINE__, (*__err)->name, ##__VA_ARGS__)
#define printerr(s, ...) _printerr("error", s, ##__VA_ARGS__)
#define printuncaughterr() _printerr("uncaught error", "%s", (*__err)->msg)

#define _errordef(n, _id) \
error* new_##n##_error_msg(char* msg) { \
    error* self = malloc(sizeof(error)); \
    self->id = _id; \
    self->name = #n; \
    self->msg = msg; \
    return self; \
} \
error* new_##n##_error() { return new_##n##_error_msg(""); }

#define errordef(n) _errordef(n, __COUNTER__ +1)

#define try(try_block, err, err_name, catch_block) { \
    error * err_name = NULL; \
    error ** __err = & err_name; \
    void __try_fn() try_block \
    __try_fn(); \
    void __catch_fn() { \
        if (err_name == NULL) return; \
        unsigned int __##err_name##_id = new_##err##_error()->id; \
        if (__##err_name##_id != 0 && __##err_name##_id != err_name->id) \
            printuncaughterr(); \
        else if (__##err_name##_id != 0 || __##err_name##_id != err_name->id) \
            catch_block \
    } \
    __catch_fn(); \
}

#define throw(e) { *__err = e; return; }

_errordef(any, 0)

용법:

errordef(my_err1)
errordef(my_err2)

try ({
    printf("Helloo\n");
    throw(new_my_err1_error_msg("hiiiii!"));
    printf("This will not be printed!\n");
}, /*catch*/ any, e, {
    printf("My lovely error: %s %s\n", e->name, e->msg);
})

printf("\n");

try ({
    printf("Helloo\n");
    throw(new_my_err2_error_msg("my msg!"));
    printf("This will not be printed!\n");
}, /*catch*/ my_err2, e, {
    printerr("%s", e->msg);
})

printf("\n");

try ({
    printf("Helloo\n");
    throw(new_my_err1_error());
    printf("This will not be printed!\n");
}, /*catch*/ my_err2, e, {
    printf("Catch %s if you can!\n", e->name);
})

산출:

Helloo
My lovely error: my_err1 hiiiii!

Helloo
/home/naheel/Desktop/aa.c:28: error: my_err2_error my msg!

Helloo
/home/naheel/Desktop/aa.c:38: uncaught error: my_err1_error 

이것은 중첩 된 함수와 __COUNTER__. gcc를 사용하는 경우 안전한 편입니다.


1

Redis는 goto를 사용하여 try / catch를 시뮬레이션합니다. IMHO는 매우 깨끗하고 우아합니다.

/* Save the DB on disk. Return REDIS_ERR on error, REDIS_OK on success. */
int rdbSave(char *filename) {
    char tmpfile[256];
    FILE *fp;
    rio rdb;
    int error = 0;

    snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
    fp = fopen(tmpfile,"w");
    if (!fp) {
        redisLog(REDIS_WARNING, "Failed opening .rdb for saving: %s",
            strerror(errno));
        return REDIS_ERR;
    }

    rioInitWithFile(&rdb,fp);
    if (rdbSaveRio(&rdb,&error) == REDIS_ERR) {
        errno = error;
        goto werr;
    }

    /* Make sure data will not remain on the OS's output buffers */
    if (fflush(fp) == EOF) goto werr;
    if (fsync(fileno(fp)) == -1) goto werr;
    if (fclose(fp) == EOF) goto werr;

    /* Use RENAME to make sure the DB file is changed atomically only
     * if the generate DB file is ok. */
    if (rename(tmpfile,filename) == -1) {
        redisLog(REDIS_WARNING,"Error moving temp DB file on the final destination: %s", strerror(errno));
        unlink(tmpfile);
        return REDIS_ERR;
    }
    redisLog(REDIS_NOTICE,"DB saved on disk");
    server.dirty = 0;
    server.lastsave = time(NULL);
    server.lastbgsave_status = REDIS_OK;
    return REDIS_OK;

werr:
    fclose(fp);
    unlink(tmpfile);
    redisLog(REDIS_WARNING,"Write error saving DB on disk: %s", strerror(errno));
    return REDIS_ERR;
}

코드가 깨졌습니다. errno시스템 호출이 실패한 직후에만 사용해야하며 나중에 세 번 호출하지 않아야합니다.

이 코드는 여러 위치에서 오류 처리 논리를 복제하고 fclose (fp)를 여러 번 호출하는 것과 같은 잘못된 작업을 수행 할 수 있습니다. 여러 레이블을 사용하고 모든 오류에 대해 하나만 사용하는 대신 해당 레이블을 사용하여 여전히 회수해야하는 항목을 인코딩 한 다음 코드에서 오류가 발생하는 위치에 따라 올바른 오류 처리 지점으로 이동하는 것이 훨씬 낫습니다.
jschultz410

1

C에서는 명시적인 오류 처리를 위해 if + goto를 수동으로 사용하여 자동 "객체 재 확보"와 함께 예외를 "시뮬레이션"할 수 있습니다.

나는 종종 다음과 같은 C 코드를 작성합니다 (오류 처리를 강조하기 위해 끓여서).

#include <assert.h>

typedef int errcode;

errcode init_or_fail( foo *f, goo *g, poo *p, loo *l )
{
    errcode ret = 0;

    if ( ( ret = foo_init( f ) ) )
        goto FAIL;

    if ( ( ret = goo_init( g ) ) )
        goto FAIL_F;

    if ( ( ret = poo_init( p ) ) )
        goto FAIL_G;

    if ( ( ret = loo_init( l ) ) )
        goto FAIL_P;

    assert( 0 == ret );
    goto END;

    /* error handling and return */

    /* Note that we finalize in opposite order of initialization because we are unwinding a *STACK* of initialized objects */

FAIL_P:
    poo_fini( p );

FAIL_G:
    goo_fini( g );

FAIL_F:
    foo_fini( f );

FAIL:
    assert( 0 != ret );

END:
    return ret;        
}

이것은 완전히 표준 ANSI C이고, 메인 라인 코드에서 오류 처리를 분리하고, C ++처럼 초기화 된 객체의 (수동) 스택 해제를 허용하며, 여기서 무슨 일이 일어나고 있는지 완전히 분명합니다. 각 지점에서 명시 적으로 실패를 테스트하기 때문에 오류가 발생할 수있는 각 위치에 특정 로깅 또는 오류 처리를 삽입하기가 더 쉽습니다.

약간의 매크로 마법이 마음에 들지 않으면 스택 추적으로 오류를 로깅하는 것과 같은 다른 작업을 수행하면서 더 간결하게 만들 수 있습니다. 예를 들면 :

#include <assert.h>
#include <stdio.h>
#include <string.h>

#define TRY( X, LABEL ) do { if ( ( X ) ) { fprintf( stderr, "%s:%d: Statement '" #X "' failed! %d, %s\n", __FILE__, __LINE__, ret, strerror( ret ) ); goto LABEL; } while ( 0 )

typedef int errcode;

errcode init_or_fail( foo *f, goo *g, poo *p, loo *l )
{
    errcode ret = 0;

    TRY( ret = foo_init( f ), FAIL );
    TRY( ret = goo_init( g ), FAIL_F );
    TRY( ret = poo_init( p ), FAIL_G );
    TRY( ret = loo_init( l ), FAIL_P );

    assert( 0 == ret );
    goto END;

    /* error handling and return */

FAIL_P:
    poo_fini( p );

FAIL_G:
    goo_fini( g );

FAIL_F:
    foo_fini( f );

FAIL:
    assert( 0 != ret );

END:
    return ret;        
}

물론 이것은 C ++ 예외 + 소멸자만큼 우아하지는 않습니다. 예를 들어, 이러한 방식으로 하나의 함수 내에 여러 오류 처리 스택을 중첩하는 것은 그리 깔끔하지 않습니다. 대신, 당신은 아마도 오류를 유사하게 처리하고 초기화 + 마무리하는 자체 포함 하위 함수로 나누고 싶을 것입니다.

이것은 또한 단일 함수 내에서만 작동하며 상위 수준 호출자가 유사한 명시 적 오류 처리 논리를 구현하지 않는 한 스택 위로 계속 올라가지 않는 반면, C ++ 예외는 적절한 핸들러를 찾을 때까지 스택 위로 계속 점프합니다. 또한 임의의 유형을 던지는 것을 허용하지 않고 대신 오류 코드 만 허용합니다.

이러한 방식으로 체계적으로 코딩하면 (즉, 단일 진입 점과 단일 종료점을 사용하여) 무엇이든 실행될 사전 및 사후 ( "최종") 논리를 삽입하는 것이 매우 쉽습니다. END 레이블 뒤에 "최종"로직을 넣습니다.


1
아주 좋아. 나는 비슷한 일을하는 경향이 있습니다. goto는이 시나리오에 적합합니다. 유일한 차이점은 마지막 "goto END"가 필요하지 않다는 것입니다. 그 지점에서 성공 리턴을 삽입하고 나머지 후에는 실패 리턴을 삽입합니다.
Neil Roy

1
고마워요 @NeilRoy goto END의 이유는 대부분의 기능이 단일 진입 점과 단일 출구 점을 갖는 것을 좋아하기 때문입니다. 이렇게하면 어떤 함수에 "마지막"논리를 추가하려는 경우 어딘가에 숨어있는 다른 숨겨진 수익이 있다는 걱정없이 항상 쉽게 할 수 있습니다. :)
jschultz410

0

Win32와 함께 C를 사용하는 경우 구조적 예외 처리 (SEH) 를 활용하여 try / catch를 시뮬레이션 할 수 있습니다.

당신이 지원하지 않는 플랫폼에서 C 사용하는 경우 setjmp()longjmp()이것 좀 봐 가지고, 예외 처리 pjsip 라이브러리를, 그것의 자신의 구현을 제공 않습니다


-1

아마도 주요 언어는 아니지만 (불행히도) APL에는 ⎕EA 연산 (대체 실행을 의미)이 있습니다.

사용법 : 'Y'⎕EA 'X'여기서 X와 Y는 문자열 또는 함수 이름으로 제공되는 코드 조각입니다.

X에 오류가 발생하면 Y (일반적으로 오류 처리)가 대신 실행됩니다.


2
안녕하세요 mappo, StackOverflow에 오신 것을 환영합니다. 흥미 롭긴하지만 질문은 구체적으로 C에서 이것을하는 것에 관한 것이 었습니다. 그래서 이것은 실제로 질문에 대한 답
luser droog
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.