C에서 setjmp 및 longjmp의 실제 사용


98

사람이 정확히 어디 나를 설명 할 수 setjmp()longjmp()기능이 임베디드 프로그래밍에서 실질적으로 사용할 수 있습니까? 나는 이것이 오류 처리 용이라는 것을 알고 있습니다. 하지만 몇 가지 사용 사례를 알고 싶습니다.


다른 프로그래밍과 같은 오류 처리 용. 사용법의 차이가 보이지 않습니까 ???
Tony The Lion


속도를 위해? 예. a) 루프보다 느리게 실행되고 b) 쉽게 최적화 할 수 없기 때문입니다 (예 : 지연 삭제 또는 두 번 삭제). 그래서 setjmp & longjmp는 분명히 지배합니다!
TheBlastOne 2013-02-05

주어진 것보다 다른 대답은 여기 stackoverflow.com/questions/7334595/... 당신은 사용할 수 있습니다 longjmp()유사한 특히 것들, 신호 처리기의 나가 BUS ERROR. 이 신호는 일반적으로 다시 시작할 수 없습니다. 임베디드 애플리케이션은 안전과 견고한 작동을 위해이 케이스를 처리 할 수 ​​있습니다.
꾸밈 소음

그리고의 성능 차이에 대한 setjmpBSD와 리눅스 사이에 참조 "타이밍 setjmp는, 및 표준의 기쁨" 사용 제안, sigsetjmp.
요안 Filippidis

답변:


84

오류 처리
다른 많은 함수에 중첩 된 함수에 오류가 있고 오류 처리가 최상위 수준 함수에서만 의미가 있다고 가정합니다.

그 사이에있는 모든 함수가 정상적으로 반환하고 반환 값이나 전역 오류 변수를 평가하여 추가 처리가 의미가 없거나 나쁠 것인지를 결정해야한다면 매우 지루하고 어색 할 것입니다.

이것이 setjmp / longjmp가 의미가있는 상황입니다. 이러한 상황은 다른 언어 (C ++, Java)의 예외가 의미가있는 상황과 유사합니다.

코 루틴
오류 처리 외에도 C에서 setjmp / longjmp가 필요한 또 다른 상황을 생각할 수 있습니다.

코 루틴 을 구현해야하는 경우 입니다.

여기에 약간의 데모 예제가 있습니다. 몇 가지 예제 코드에 대한 Sivaprasad Palas의 요청을 충족하고 TheBlastOne의 질문에 setjmp / longjmp가 코 루틴 구현을 지원하는 방법에 대한 답변을 제공하기를 바랍니다 (비표준 또는 새로운 동작을 기반으로하지 않는 한).

편집 :
그것은 실제로 일 수 있었다 이다 을 할 수있는 정의되지 않은 동작이 longjmp 아래로 (MikeMB의 의견을 참조, 나는 아직 그것을 확인하는 기회가 없었어요하지만) 호출 스택.

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

jmp_buf bufferA, bufferB;

void routineB(); // forward declaration 

void routineA()
{
    int r ;

    printf("(A1)\n");

    r = setjmp(bufferA);
    if (r == 0) routineB();

    printf("(A2) r=%d\n",r);

    r = setjmp(bufferA);
    if (r == 0) longjmp(bufferB, 20001);

    printf("(A3) r=%d\n",r);

    r = setjmp(bufferA);
    if (r == 0) longjmp(bufferB, 20002);

    printf("(A4) r=%d\n",r);
}

void routineB()
{
    int r;

    printf("(B1)\n");

    r = setjmp(bufferB);
    if (r == 0) longjmp(bufferA, 10001);

    printf("(B2) r=%d\n", r);

    r = setjmp(bufferB);
    if (r == 0) longjmp(bufferA, 10002);

    printf("(B3) r=%d\n", r);

    r = setjmp(bufferB);
    if (r == 0) longjmp(bufferA, 10003);
}


int main(int argc, char **argv) 
{
    routineA();
    return 0;
}

다음 그림은 실행 흐름을 보여줍니다.
실행의 흐름

경고 참고
setjmp / longjmp를 사용할 때 종종 고려되지 않는 지역 변수의 유효성에 영향을 미친다는 점에 유의하십시오 .
Cf. 이 주제에 대한질문 .


2
setjmp가 준비하고 longjmp가 현재 호출 범위에서 setjmp 범위로 다시 점프를 실행하므로 코 루틴 구현을 어떻게 지원할까요? 나는 어떻게 누군가가 longjmp´d 루틴의 실행을 계속할 수 있는지 보지 못했습니다.
TheBlastOne 2013

2
@TheBlastOne Wikipedia 기사를 참조하십시오 . 당신이 실행 setjmp하기 전에 실행을 계속할 수 있습니다 longjmp. 이것은 비표준입니다.
Potatoswatter 2013

10
코 루틴은 예제에 표시된 것과 동일하지 않고 별도의 스택에서 실행되어야합니다. 로 routineAroutineB같은 스택을 사용, 그것은 매우 원시적 인 코 루틴에 적용됩니다. 경우 routineA통화가 깊게 중첩 routineC에 대한 첫 번째 호출 후 routineBroutineC실행 routineB코 루틴으로, 다음 routineB도의 리턴 스택 (뿐만 아니라 지역 변수)를 파괴 할 수 있습니다 routineC. 따라서 배타적 스택을 할당하지 않고 ( ? alloca()를 호출 한 후 rountineB)이 예제를 레시피로 사용하면 심각한 문제가 발생합니다.
Tino

7
귀하의 답변에서 (A에서 B로) 호출 스택 아래로 점프하는 것은 정의되지 않은 동작이라고 언급하십시오.
MikeMB

1
그리고 각주 248)에는 "예를 들어 return 문을 실행하거나 다른 longjmp 호출로 인해 중첩 된 호출 집합의 초기에 함수에서 setjmp 호출로의 전송이 발생했기 때문에"라고 표시됩니다. 따라서 함수에서 longjmp 함수를 호출 스택보다 더 높은 지점으로 호출하면 해당 함수가 종료되므로 나중에 다시 점프하는 것이 UB입니다.
MikeMB 2015

18

이론은 오류 처리에 사용할 수 있으므로 체인의 모든 함수에서 오류를 처리 할 필요없이 깊이 중첩 된 호출 체인에서 벗어날 수 있습니다.

모든 영리한 이론과 마찬가지로 이것은 현실을 만날 때 무너집니다. 중간 함수는 메모리를 할당하고, 잠금을 잡고, 파일을 열고, 정리가 필요한 모든 종류의 작업을 수행합니다. 따라서 실제로 setjmp/ longjmp일반적으로 환경 (일부 임베디드 플랫폼)을 완전히 제어 할 수있는 매우 제한된 상황을 제외하고는 나쁜 생각입니다.

내 경험상 대부분의 경우 setjmp/ 사용 longjmp이 작동 한다고 생각할 때마다 프로그램은 명확하고 간단하여 호출 체인의 모든 중간 함수 호출이 오류 처리를 수행 exit할 수 있습니다. 오류가 발생했습니다.


3
를보세요 libjpeg. C ++에서와 같이 대부분의 C 루틴 모음은 struct *집합으로서 무언가에 대해 작동합니다. 중간 함수 메모리 할당을 로컬로 저장하는 대신 구조에 저장할 수 있습니다. 이를 통해 longjmp()핸들러가 메모리를 해제 할 수 있습니다 . 또한, 모든 C ++ 컴파일러가 20 년이 지난 후에도 여전히 생성되는 블라스팅 된 예외 테이블이 많지 않습니다.
꾸밈 소음

Like every clever theory this falls apart when meeting reality.실제로 임시 할당 등은 호출 스택에서 여러 번 (종료하기 전에 일종의 정리를 수행해야하는 모든 함수에 대해 한 번, 그런 다음 "예외를 다시 발생"해야하기 longjmp()때문에) 까다로워 setjmp()집니다. 에 의해 longjmp()문맥에 보내고 그것은 처음에)받은 것이다. 에서 해당 리소스를 수정 하면 에서이를 막기 위해 setjmp()선언해야하기 때문에 더 악화됩니다 . volatilelongjmp()
sevko 2015

10

의 조합 setjmplongjmp"최고 강점입니다 goto." 극도로주의해서 사용하십시오. 그러나 다른 사람들이 설명했듯이 a longjmpget me back to the beginning18 개 계층의 함수에 대해 오류 메시지를 다시 세울 필요없이 신속하게 원할 때 불쾌한 오류 상황에서 벗어나는 데 매우 유용 합니다.

그러나, goto하지만 더 나쁜 것은 이것을 사용하는 방법에 정말 조심해야합니다. A longjmp는 코드의 시작 부분으로 돌아갑니다. 그것은 사이에 변경되었을 수 있습니다 다른 모든 국가에 영향을주지 않습니다 setjmp곳과 점점 다시 setjmp시작합니다. 따라서 할당, 잠금, 반 초기화 된 데이터 구조 등 setjmp은 호출 된 위치 로 돌아갈 때 여전히 할당되고 잠기 며 반 초기화됩니다 . 이것은 당신이 이것을하는 곳을 정말로 돌봐야한다는 것을 의미 longjmp합니다. 더 많은 문제를 일으키지 않고 전화하는 것이 정말 괜찮습니다 . 물론, 다음 작업이 "재부팅"(오류에 대한 메시지를 저장 한 후) 인 경우-예를 들어 하드웨어가 불량 상태임을 발견 한 임베디드 시스템에서, 예를 들어 정상입니다.

또한 본 setjmp/ longjmp아주 기본적인 스레딩 메커니즘을 제공하는 데 사용됩니다. 그러나 이것은 매우 특별한 경우이며 "표준"스레드가 작동하는 방식은 확실히 아닙니다.

편집 : 물론 "정리 처리"에 코드를 추가 할 수 있습니다. 이는 C ++가 컴파일 된 코드에 예외 지점을 저장 한 다음 예외가 발생한 항목과 정리가 필요한 항목을 아는 것과 같은 방식입니다. 여기에는 일종의 함수 포인터 테이블과 "여기서 아래에서 뛰어 내리면이 인수를 사용하여이 함수를 호출하십시오"라는 저장이 포함됩니다. 이 같은:

struct 
{
    void (*destructor)(void *ptr);
};


void LockForceUnlock(void *vlock)
{
   LOCK* lock = vlock;
}


LOCK func_lock;


void func()
{
   ref = add_destructor(LockForceUnlock, mylock);
   Lock(func_lock)
   ... 
   func2();   // May call longjmp. 

   Unlock(func_lock);
   remove_destructor(ref);
}

이 시스템을 사용하면 "C ++와 같은 완전한 예외 처리"를 수행 할 수 있습니다. 그러나 그것은 매우 지저분하고 잘 작성된 코드에 의존합니다.


물론 이론적으로는 setjmp모든 초기화를 보호하기 위해 호출 하여 깨끗한 예외 처리를 구현할 수 있습니다. C ++… 스레딩에 사용하는 것은 비표준이라는 점을 언급 할 가치가 있습니다.
Potatoswatter 2013

8

임베디드를 언급했기 때문에 사용하지 않는 경우에 주목할 가치가 있다고 생각 합니다. 코딩 표준이이를 금지하는 경우입니다. 예를 들어 MISRA (MISRA-C : 2004 : Rule 20.7) 및 JFS (AV 규칙 20) : "setjmp 매크로 및 longjmp 함수는 사용되지 않습니다."


8

setjmplongjmp단위 테스트에서 매우 유용 할 수 있습니다.

다음 모듈을 테스트한다고 가정합니다.

#include <stdlib.h>

int my_div(int x, int y)
{
    if (y==0) exit(2);
    return x/y;
}

일반적으로 테스트 할 함수가 다른 함수를 호출하는 경우 특정 흐름을 테스트하기 위해 실제 함수가 수행하는 작업을 모방하는 호출 할 스텁 함수를 선언 할 수 있습니다. 그러나이 경우 exit반환되지 않는 함수가 호출 됩니다. 스텁은 어떻게 든이 동작을 에뮬레이트해야합니다. setjmp그리고 longjmp당신을 위해 할 수 있습니다.

이 기능을 테스트하기 위해 다음 테스트 프로그램을 만들 수 있습니다.

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

// redefine assert to set a boolean flag
#ifdef assert
#undef assert
#endif
#define assert(x) (rslt = rslt && (x))

// the function to test
int my_div(int x, int y);

// main result return code used by redefined assert
static int rslt;

// variables controling stub functions
static int expected_code;
static int should_exit;
static jmp_buf jump_env;

// test suite main variables
static int done;
static int num_tests;
static int tests_passed;

//  utility function
void TestStart(char *name)
{
    num_tests++;
    rslt = 1;
    printf("-- Testing %s ... ",name);
}

//  utility function
void TestEnd()
{
    if (rslt) tests_passed++;
    printf("%s\n", rslt ? "success" : "fail");
}

// stub function
void exit(int code)
{
    if (!done)
    {
        assert(should_exit==1);
        assert(expected_code==code);
        longjmp(jump_env, 1);
    }
    else
    {
        _exit(code);
    }
}

// test case
void test_normal()
{
    int jmp_rval;
    int r;

    TestStart("test_normal");
    should_exit = 0;
    if (!(jmp_rval=setjmp(jump_env)))
    {
        r = my_div(12,3);
    }

    assert(jmp_rval==0);
    assert(r==4);
    TestEnd();
}

// test case
void test_div0()
{
    int jmp_rval;
    int r;

    TestStart("test_div0");
    should_exit = 1;
    expected_code = 2;
    if (!(jmp_rval=setjmp(jump_env)))
    {
        r = my_div(2,0);
    }

    assert(jmp_rval==1);
    TestEnd();
}

int main()
{
    num_tests = 0;
    tests_passed = 0;
    done = 0;
    test_normal();
    test_div0();
    printf("Total tests passed: %d\n", tests_passed);
    done = 1;
    return !(tests_passed == num_tests);
}

이 예제에서는 setjmp테스트 할 함수를 입력하기 전에 를 사용한 다음 스텁에서 exit호출 longjmp하여 테스트 케이스로 직접 돌아갑니다.

또한 재정의 exit에는 실제로 프로그램을 종료하고이를 호출 _exit할 것인지 확인하는 특수 변수가 있습니다 . 이렇게하지 않으면 테스트 프로그램이 완전히 종료되지 않을 수 있습니다.


6

, 및 시스템 함수를 사용하여 C에서 Java와 유사한 예외 처리 메커니즘 을 작성했습니다 . 사용자 정의 예외를 포착하지만 . 함수 호출에 걸쳐 작동하는 예외 처리 블록의 무한 중첩 기능을 제공하며 가장 일반적인 두 가지 스레딩 구현을 지원합니다. 링크 타임 상속을 특징으로하는 예외 클래스의 트리 계층 구조를 정의 할 수 있으며, 명령문은이 트리를 탐색하여 캐치 또는 전달이 필요한지 확인합니다.setjmp()longjmp()SIGSEGVcatch

다음은 이것을 사용하여 코드가 어떻게 보이는지에 대한 샘플입니다.

try
{
    *((int *)0) = 0;    /* may not be portable */
}
catch (SegmentationFault, e)
{
    long f[] = { 'i', 'l', 'l', 'e', 'g', 'a', 'l' };
    ((void(*)())f)();   /* may not be portable */
}
finally
{
    return(1 / strcmp("", ""));
}

다음은 많은 논리를 포함하는 포함 파일의 일부입니다.

#ifndef _EXCEPT_H
#define _EXCEPT_H

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include "Lifo.h"
#include "List.h"

#define SETJMP(env)             sigsetjmp(env, 1)
#define LONGJMP(env, val)       siglongjmp(env, val)
#define JMP_BUF                 sigjmp_buf

typedef void (* Handler)(int);

typedef struct _Class *ClassRef;        /* exception class reference */
struct _Class
{
    int         notRethrown;            /* always 1 (used by throw()) */
    ClassRef    parent;                 /* parent class */
    char *      name;                   /* this class name string */
    int         signalNumber;           /* optional signal number */
};

typedef struct _Class Class[1];         /* exception class */

typedef enum _Scope                     /* exception handling scope */
{
    OUTSIDE = -1,                       /* outside any 'try' */
    INTERNAL,                           /* exception handling internal */
    TRY,                                /* in 'try' (across routine calls) */
    CATCH,                              /* in 'catch' (idem.) */
    FINALLY                             /* in 'finally' (idem.) */
} Scope;

typedef enum _State                     /* exception handling state */
{
    EMPTY,                              /* no exception occurred */
    PENDING,                            /* exception occurred but not caught */
    CAUGHT                              /* occurred exception caught */
} State;

typedef struct _Except                  /* exception handle */
{
    int         notRethrown;            /* always 0 (used by throw()) */
    State       state;                  /* current state of this handle */
    JMP_BUF     throwBuf;               /* start-'catching' destination */
    JMP_BUF     finalBuf;               /* perform-'finally' destination */
    ClassRef    class;                  /* occurred exception class */
    void *      pData;                  /* exception associated (user) data */
    char *      file;                   /* exception file name */
    int         line;                   /* exception line number */
    int         ready;                  /* macro code control flow flag */
    Scope       scope;                  /* exception handling scope */
    int         first;                  /* flag if first try in function */
    List *      checkList;              /* list used by 'catch' checking */
    char*       tryFile;                /* source file name of 'try' */
    int         tryLine;                /* source line number of 'try' */

    ClassRef    (*getClass)(void);      /* method returning class reference */
    char *      (*getMessage)(void);    /* method getting description */
    void *      (*getData)(void);       /* method getting application data */
    void        (*printTryTrace)(FILE*);/* method printing nested trace */
} Except;

typedef struct _Context                 /* exception context per thread */
{
    Except *    pEx;                    /* current exception handle */
    Lifo *      exStack;                /* exception handle stack */
    char        message[1024];          /* used by ExceptGetMessage() */
    Handler     sigAbrtHandler;         /* default SIGABRT handler */
    Handler     sigFpeHandler;          /* default SIGFPE handler */
    Handler     sigIllHandler;          /* default SIGILL handler */
    Handler     sigSegvHandler;         /* default SIGSEGV handler */
    Handler     sigBusHandler;          /* default SIGBUS handler */
} Context;

extern Context *        pC;
extern Class            Throwable;

#define except_class_declare(child, parent) extern Class child
#define except_class_define(child, parent)  Class child = { 1, parent, #child }

except_class_declare(Exception,           Throwable);
except_class_declare(OutOfMemoryError,    Exception);
except_class_declare(FailedAssertion,     Exception);
except_class_declare(RuntimeException,    Exception);
except_class_declare(AbnormalTermination, RuntimeException);  /* SIGABRT */
except_class_declare(ArithmeticException, RuntimeException);  /* SIGFPE */
except_class_declare(IllegalInstruction,  RuntimeException);  /* SIGILL */
except_class_declare(SegmentationFault,   RuntimeException);  /* SIGSEGV */
except_class_declare(BusError,            RuntimeException);  /* SIGBUS */


#ifdef  DEBUG

#define CHECKED                                                         \
        static int checked

#define CHECK_BEGIN(pC, pChecked, file, line)                           \
            ExceptCheckBegin(pC, pChecked, file, line)

#define CHECK(pC, pChecked, class, file, line)                          \
                 ExceptCheck(pC, pChecked, class, file, line)

#define CHECK_END                                                       \
            !checked

#else   /* DEBUG */

#define CHECKED
#define CHECK_BEGIN(pC, pChecked, file, line)           1
#define CHECK(pC, pChecked, class, file, line)          1
#define CHECK_END                                       0

#endif  /* DEBUG */


#define except_thread_cleanup(id)       ExceptThreadCleanup(id)

#define try                                                             \
    ExceptTry(pC, __FILE__, __LINE__);                                  \
    while (1)                                                           \
    {                                                                   \
        Context *       pTmpC = ExceptGetContext(pC);                   \
        Context *       pC = pTmpC;                                     \
        CHECKED;                                                        \
                                                                        \
        if (CHECK_BEGIN(pC, &checked, __FILE__, __LINE__) &&            \
            pC->pEx->ready && SETJMP(pC->pEx->throwBuf) == 0)           \
        {                                                               \
            pC->pEx->scope = TRY;                                       \
            do                                                          \
            {

#define catch(class, e)                                                 \
            }                                                           \
            while (0);                                                  \
        }                                                               \
        else if (CHECK(pC, &checked, class, __FILE__, __LINE__) &&      \
                 pC->pEx->ready && ExceptCatch(pC, class))              \
        {                                                               \
            Except *e = LifoPeek(pC->exStack, 1);                       \
            pC->pEx->scope = CATCH;                                     \
            do                                                          \
            {

#define finally                                                         \
            }                                                           \
            while (0);                                                  \
        }                                                               \
        if (CHECK_END)                                                  \
            continue;                                                   \
        if (!pC->pEx->ready && SETJMP(pC->pEx->finalBuf) == 0)          \
            pC->pEx->ready = 1;                                         \
        else                                                            \
            break;                                                      \
    }                                                                   \
    ExceptGetContext(pC)->pEx->scope = FINALLY;                         \
    while (ExceptGetContext(pC)->pEx->ready > 0 || ExceptFinally(pC))   \
        while (ExceptGetContext(pC)->pEx->ready-- > 0)

#define throw(pExceptOrClass, pData)                                    \
    ExceptThrow(pC, (ClassRef)pExceptOrClass, pData, __FILE__, __LINE__)

#define return(x)                                                       \
    {                                                                   \
        if (ExceptGetScope(pC) != OUTSIDE)                              \
        {                                                               \
            void *      pData = malloc(sizeof(JMP_BUF));                \
            ExceptGetContext(pC)->pEx->pData = pData;                   \
            if (SETJMP(*(JMP_BUF *)pData) == 0)                         \
                ExceptReturn(pC);                                       \
            else                                                        \
                free(pData);                                            \
        }                                                               \
        return x;                                                       \
    }

#define pending                                                         \
    (ExceptGetContext(pC)->pEx->state == PENDING)

extern Scope    ExceptGetScope(Context *pC);
extern Context *ExceptGetContext(Context *pC);
extern void     ExceptThreadCleanup(int threadId);
extern void     ExceptTry(Context *pC, char *file, int line);
extern void     ExceptThrow(Context *pC, void * pExceptOrClass,
                            void *pData, char *file, int line);
extern int      ExceptCatch(Context *pC, ClassRef class);
extern int      ExceptFinally(Context *pC);
extern void     ExceptReturn(Context *pC);
extern int      ExceptCheckBegin(Context *pC, int *pChecked,
                                 char *file, int line);
extern int      ExceptCheck(Context *pC, int *pChecked, ClassRef class,
                            char *file, int line);


#endif  /* _EXCEPT_H */

신호 처리 및 일부 부기 로직을 ​​포함하는 C 모듈도 있습니다.

구현하기가 매우 까다로 웠습니다. 저는 가능한 한 Java에 가깝게 만들려고 정말 노력했습니다. 나는 단지 C로 얼마나 멀리 왔는지 놀랐습니다.

관심이 있으시면 외쳐주세요.


1
커스텀 예외에 대한 실제 컴파일러 지원없이 이것이 가능하다는 것에 놀랐습니다. 하지만 정말 흥미로운 것은 신호가 예외로 변환되는 방식입니다.
Paul Stelian 2019

한 가지 물어볼 것입니다. 결국 잡히지 않는 예외는 어떻습니까? main ()은 어떻게 종료됩니까?
Paul Stelian 2019

1
@PaulStelian 그리고, 잡히지 않은 예외에서 어떻게 종료 할 것인지에 대한 답변main()있습니다. 이 답변 :-) upvote에하십시오
의미 - 중요한

1
@PaulStelian 아, 이제 무슨 뜻인지 알겠습니다. 잡히지 않은 런타임 예외가 다시 발생하여 일반 (플랫폼에 따라 다름) 답변이 적용됩니다. 포착되지 않은 사용자 정의 예외가 인쇄되고 무시되었습니다. README의Progagation 섹션을 참조하십시오 .1999 년 4 월 코드를 GitHub에 게시했습니다 (편집 된 답변의 링크 참조). 보세요; 부수기 힘든 너트였다. 당신이 생각하는 것을 듣고 좋을 것입니다.
의미 문제

2
README를 짧게 보았습니다. 꽤 좋은 것입니다. 따라서 기본적으로 가장 바깥 쪽 try 블록으로 전파되고 JavaScript의 비동기 함수와 유사하게보고됩니다. 좋은. 나중에 소스 코드 자체를 살펴 보겠습니다.
Paul Stelian

1

손 아래로, setjmp / longjmp의 가장 중요한 사용은 "비 로컬 이동 점프"역할을한다는 것입니다. Goto 명령 (및 for 및 while 루프에서 goto를 사용해야하는 드문 경우)은 동일한 범위에서 가장 안전하게 사용됩니다. goto를 사용하여 범위 (또는 자동 할당)를 건너 뛰면 프로그램 스택이 손상 될 가능성이 높습니다. setjmp / longjmp는 점프하려는 위치에 스택 정보를 저장하여이를 방지합니다. 그런 다음 점프 할 때이 스택 정보를로드합니다. 이 기능이 없으면 C 프로그래머는 setjmp / longjmp 만 해결할 수있는 문제를 해결하기 위해 어셈블리 프로그래밍으로 전환해야 할 가능성이 큽니다. 존재하는 신에게 감사합니다. C 라이브러리의 모든 것은 매우 중요합니다. 필요할 때 알 수 있습니다.


"C 라이브러리의 모든 것은 매우 중요합니다." 더 이상 사용되지 않는 항목과 로케일처럼 결코 좋지 않은 항목이 많이 있습니다.
qwr
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.