Praktyczne wykorzystanie setjmp i longjmp w C

99

Czy ktoś może mi wyjaśnić, gdzie dokładnie setjmp()i longjmp()funkcje można praktycznie wykorzystać w programowaniu wbudowanym? Wiem, że te służą do obsługi błędów. Ale chciałbym poznać kilka przypadków użycia.

Pala
źródło
Do obsługi błędów jak w każdym innym programowaniu. Nie widzę różnicy w użyciu ???
Tony The Lion
3
I oczywiście thedailywtf.com/Articles/Longjmp--FOR-SPEED!!!.aspx
Daniel Fischer,
Dla prędkości? Tak. Ponieważ a) działa wolniej niż pętla i b) ponieważ nie można go łatwo zoptymalizować (jak usunięcie opóźnienia lub dwóch). Więc setjmp i longjmp wyraźnie rządzą!
TheBlastOne
Inną odpowiedzią niż te podane jest tutaj stackoverflow.com/questions/7334595/… Możesz użyć longjmp()do wyjścia z obsługi sygnału, szczególnie rzeczy takie jak a BUS ERROR. Ten sygnał zwykle nie może zostać ponownie uruchomiony. Wbudowana aplikacja może chcieć obsłużyć ten przypadek w celu zapewnienia bezpieczeństwa i niezawodnego działania.
bezartowy hałas
Jeśli chodzi o różnice w wydajności setjmpmiędzy BSD i Linuksem, zobacz „Timing setjmp, and the Joy of Standards” , który sugeruje użycie sigsetjmp.
Ioannis Filippidis

Odpowiedzi:

84

Obsługa błędów
Załóżmy, że w funkcji zagnieżdżonej w wielu innych funkcjach występuje błąd, a obsługa błędów ma sens tylko w funkcji najwyższego poziomu.

Byłoby bardzo uciążliwe i niewygodne, gdyby wszystkie funkcje pomiędzy nimi musiały zwracać normalnie i oceniać zwracane wartości lub globalną zmienną błędu, aby określić, że dalsze przetwarzanie nie ma sensu lub nawet byłoby złe.

To sytuacja, w której setjmp / longjmp ma sens. Te sytuacje są podobne do sytuacji, w których wyjątki w innych językach (C ++, Java) mają sens.

Korekty
Oprócz obsługi błędów przychodzi mi do głowy jeszcze inna sytuacja, w której potrzebujesz setjmp / longjmp w C:

Tak jest w przypadku, gdy musisz wdrożyć programy .

Oto mały przykład demo. Mam nadzieję, że spełnia prośbę Sivaprasad Palas o przykładowy kod i odpowiada na pytanie TheBlastOne, w jaki sposób setjmp / longjmp obsługuje implementację procedur (o ile widzę, że nie opiera się na żadnym niestandardowym lub nowym zachowaniu).

EDIT:
To może być to, że faktycznie jest niezdefiniowane zachowanie zrobić longjmp dół callstack (patrz komentarz o MikeMB, choć jeszcze nie miałem okazji do sprawdzenia tego).

#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;
}

Poniższy rysunek przedstawia przebieg wykonania:
przepływ egzekucji

Uwaga ostrzegawcza
Używając setjmp / longjmp, należy pamiętać, że mają one wpływ na ważność zmiennych lokalnych, które często nie są brane pod uwagę.
Por. moje pytanie na ten temat .

Twaróg
źródło
2
Ponieważ setjmp przygotowuje, a longjmp wykonuje skok z zakresu bieżącego wywołania z powrotem do zakresu setjmp, w jaki sposób miałoby to wspierać implementację korektorów? Nie rozumiem, jak można by kontynuować wykonywanie procedury, która wyszła z longjmp.
TheBlastOne
2
@TheBlastOne Zobacz artykuł w Wikipedii . Możesz kontynuować egzekucję, jeśli jesteś setjmpprzed tobą longjmp. To jest niestandardowe.
Potatoswatter
11
Korekty muszą działać na osobnych stosach, a nie na tym samym, co pokazano w przykładzie. Jako routineAi routineBużywaj tego samego stosu, działa tylko dla bardzo prymitywnych coroutines. Jeśli routineAwywołuje głęboko zagnieżdżone routineCpo pierwszym wywołaniu do routineBi routineCdziała to routineBjako coroutine, routineBmoże nawet zniszczyć stos zwrotny (nie tylko zmienne lokalne) routineC. Więc bez przydzielania ekskluzywnego stosu ( alloca()po sprawdzeniu rountineB?), Będziesz miał poważne kłopoty z tym przykładem, jeśli użyjesz go jako przepisu.
Tino
7
Proszę wspomnieć w swojej odpowiedzi, że przeskakiwanie w dół stosu wywołań (z A do B) jest niezdefiniowanym zachowaniem).
MikeMB
2
To jest rzeczywiście nieokreślone. Będziesz musiał uruchomić każdą funkcję na swoim własnym niezależnym stosie, aby z wdziękiem przełączać konteksty
Curious
18

Teoria mówi, że można ich używać do obsługi błędów, dzięki czemu można wyskoczyć z głęboko zagnieżdżonego łańcucha wywołań bez konieczności radzenia sobie z błędami w każdej funkcji w łańcuchu.

Jak każda mądra teoria, ta rozpada się, gdy spotyka się z rzeczywistością. Twoje funkcje pośrednie będą alokować pamięć, przechwytywać blokady, otwierać pliki i robić różne rzeczy, które wymagają czyszczenia. Tak więc w praktyce setjmp/ longjmpsą zwykle złym pomysłem, z wyjątkiem bardzo nielicznych sytuacji, w których masz całkowitą kontrolę nad swoim środowiskiem (niektóre platformy wbudowane).

Z mojego doświadczenia wynika, że ​​w większości przypadków, gdy myślisz, że użycie setjmp/ longjmpzadziała, twój program jest na tyle jasny i prosty, że każde wywołanie funkcji pośredniej w łańcuchu wywołań może obsługiwać błędy, lub jest tak niechlujne i niemożliwe do naprawienia, że ​​powinieneś zrobić, exitgdy napotkać błąd.

Sztuka
źródło
3
Proszę spojrzeć libjpeg. Podobnie jak w C ++, większość kolekcji procedur C struct *wykorzystuje coś jako kolektyw. Zamiast przechowywać alokacje pamięci funkcji pośrednich jako lokalne, można je przechowywać w strukturze. Pozwala to longjmp()programowi obsługi zwolnić pamięć. Ponadto nie ma tak wielu przeklętych tabel wyjątków, które wszystkie kompilatory C ++ nadal generują 20 lat po fakcie.
bezartowy hałas
Like every clever theory this falls apart when meeting reality.Rzeczywiście, tymczasowa alokacja i tym podobne sprawiają, longjmp()że jest to trudne, ponieważ wtedy musisz setjmp()wielokrotnie znajdować się na stosie wywołań (raz na każdą funkcję, która musi wykonać jakieś czyszczenie przed zakończeniem, a następnie musi „ponownie zgłosić wyjątek” biorąc pod longjmp()uwagę kontekst, który otrzymała na początku). Jeszcze gorzej jest, jeśli te zasoby zostaną zmodyfikowane po tym setjmp(), jak musisz je zadeklarować, volatileaby zapobiec longjmp()ich zbiciu.
sevko
10

Połączenie setjmpi longjmpto „super siła goto”. Używaj z EKSTREMALNĄ ostrożnością. Jednak, jak wyjaśnili inni, a longjmpjest bardzo przydatny do wyjścia z nieprzyjemnej sytuacji błędu, gdy chcesz get me back to the beginningszybko, zamiast przesyłać komunikat o błędzie dla 18 warstw funkcji.

Jednak tak jak goto, ale gorzej, musisz NAPRAWDĘ uważać, jak tego używasz. A longjmpspowoduje po prostu powrót do początku kodu. Nie wpłynie to na wszystkie inne stany, które mogły ulec zmianie między setjmpdniem a powrotem do miejsca setjmprozpoczęcia. Tak więc alokacje, blokady, w połowie zainicjowane struktury danych itp. Są nadal przydzielane, blokowane i w połowie inicjowane, gdy wracasz do miejsca, w którym setjmpzostało wywołane. Oznacza to, że musisz naprawdę dbać o miejsca, w których to robisz, aby NAPRAWDĘ zadzwonić longjmpbez powodowania WIĘCEJ problemów. Oczywiście, jeśli następną rzeczą, jaką zrobisz, jest „ponowne uruchomienie” (być może po zapisaniu komunikatu o błędzie) - w systemie wbudowanym, w którym odkryłeś, że sprzęt jest na przykład w złym stanie, to w porządku.

Widziałem również setjmp/ longjmpkorzystałem z bardzo podstawowych mechanizmów gwintowania. Ale to dość szczególny przypadek - a na pewno nie sposób działania „standardowych” wątków.

Edycja: Można oczywiście dodać kod, aby „zająć się czyszczeniem”, w ten sam sposób, w jaki C ++ przechowuje punkty wyjątków w skompilowanym kodzie, a następnie wie, co spowodowało wyjątek, a co wymaga oczyszczenia. Wymagałoby to pewnego rodzaju tablicy wskaźników funkcji i przechowywania „jeśli wyskoczymy stąd z dołu, wywołaj tę funkcję z tym argumentem”. Coś takiego:

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);
}

W tym systemie można wykonać „pełną obsługę wyjątków, jak w C ++”. Ale jest dość chaotyczny i polega na dobrze napisanym kodzie.

Mats Petersson
źródło
+1, oczywiście, teoretycznie można by zaimplementować czystą obsługę wyjątków przez wywołanie setjmpochrony każdej inicjalizacji, a la C ++… i warto wspomnieć, że używanie go do tworzenia wątków jest niestandardowe.
Potatoswatter
8

Ponieważ wspomniałeś o osadzeniu, myślę, że warto zwrócić uwagę na przypadek nieużywania : kiedy zabrania tego twój standard kodowania. Na przykład MISRA (MISRA-C: 2004: Rule 20.7) i JFS (AV Rule 20): „Makro setjmp i funkcja longjmp nie powinny być używane”.

Clement J.
źródło
8

setjmpi longjmpmoże być bardzo przydatne w testowaniu jednostkowym.

Załóżmy, że chcemy przetestować następujący moduł:

#include <stdlib.h>

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

Zwykle, jeśli testowana funkcja wywołuje inną funkcję, możesz zadeklarować funkcję pośredniczącą, która będzie naśladować to, co robi rzeczywista funkcja, aby przetestować określone przepływy. W tym przypadku jednak wywołuje funkcję, exitktóra nie zwraca. Kod stub musi w jakiś sposób naśladować to zachowanie. setjmpi longjmpmogę to dla Ciebie zrobić.

Aby przetestować tę funkcję, możemy stworzyć następujący program testowy:

#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);
}

W tym przykładzie użyjesz setjmpprzed wejściem do funkcji do testowania, a następnie exitwywołasz skrót , longjmpaby powrócić bezpośrednio do przypadku testowego.

Zauważ również, że przedefiniowana exitma specjalną zmienną, którą sprawdza, czy rzeczywiście chcesz wyjść z programu i wywołuje _exitw tym celu. Jeśli tego nie zrobisz, program testowy może nie zakończyć się poprawnie.

dbush
źródło
7

Napisałem Java podobną wyjątkiem mechanizmu manipulacyjną w C z użyciem setjmp(), longjmp()oraz funkcji systemowych. Wyłapuje niestandardowe wyjątki, ale także sygnały takie jak SIGSEGV. Oferuje nieskończone zagnieżdżanie bloków obsługi wyjątków, które działa na wywołania funkcji i obsługuje dwie najpopularniejsze implementacje wątków. Umożliwia zdefiniowanie hierarchii drzewiastej klas wyjątków, które mają dziedziczenie w czasie łączenia, a catchinstrukcja przechodzi przez to drzewo, aby sprawdzić, czy musi zostać przechwycone lub przekazane dalej.

Oto przykład, jak wygląda kod przy użyciu tego:

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("", ""));
}

A oto część pliku dołączanego, która zawiera dużo logiki:

#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 */

Istnieje również moduł C, który zawiera logikę do obsługi sygnałów i trochę księgowości.

To było niezwykle trudne do wdrożenia, mogę powiedzieć i prawie zrezygnowałem. Naprawdę starałem się, aby było jak najbliżej Javy; Zaskakujące było, jak daleko zaszedłem mając tylko C.

Daj mi znać, jeśli jesteś zainteresowany.

znaczenie ma znaczenie
źródło
1
Jestem zaskoczony, że jest to możliwe bez faktycznej obsługi kompilatora dla niestandardowych wyjątków. Ale naprawdę interesujące jest to, w jaki sposób sygnały przekształcają się w wyjątki.
Paul Stelian
Zapytam jedno: a co z wyjątkami, które nigdy nie zostaną złapane? Jak zakończy się main ()?
Paul Stelian
1
@PaulStelian A oto twoja odpowiedź na pytanie, w jaki sposób main()wyjdzie z niezłapanego wyjątku . Proszę, zagłosuj na tę odpowiedź :-)
znaczenie-ma znaczenie
1
@PaulStelian Ah, teraz rozumiem, co masz na myśli. Wydaje mi się, że wyjątki w czasie wykonywania, które nie są przechwytywane, zostały ponownie zgłoszone, więc zastosowanie ma odpowiedź ogólna (zależna od platformy). Nie wychwycone wyjątki niestandardowe zostały wydrukowane i zignorowane. Zobacz Progagationsekcję w README. Wysłałem swój kod z kwietnia 1999 na GitHub (zobacz link w zredagowanej odpowiedzi). Spójrz; to był twardy orzech do zgryzienia. Byłoby miło usłyszeć, co myślisz.
znaczenie-ma znaczenie
2
Rzuciłem krótkie spojrzenie na README, całkiem niezły. Zasadniczo więc propaguje się do najbardziej zewnętrznego bloku try i jest zgłaszany, podobnie jak funkcje asynchroniczne JavaScript. Ładny. Później przyjrzę się samemu kodowi źródłowemu.
Paul Stelian
1

Bez dwóch zdań, najważniejszym zastosowaniem setjmp / longjmp jest to, że działa jako „nielokalny skok goto”. Polecenie Goto (i są rzadkie przypadki, w których będziesz musiał użyć pętli goto over for i while) jest najczęściej używane - bezpiecznie w tym samym zakresie. Jeśli używasz goto do przeskakiwania między zakresami (lub automatycznej alokacji), najprawdopodobniej uszkodzisz stos swojego programu. setjmp / longjmp pozwala tego uniknąć, zapisując informacje o stosie w miejscu, do którego chcesz przejść. Następnie podczas skoku ładuje informacje o stosie. Bez tej funkcji programiści C najprawdopodobniej musieliby zwrócić się do programowania w asemblerze, aby rozwiązać problemy, które może rozwiązać tylko setjmp / longjmp. Dzięki Bogu, że istnieje. Wszystko w bibliotece C jest niezwykle ważne. Dowiesz się, kiedy tego potrzebujesz.

AndreGraveler
źródło
1
„Wszystko w bibliotece C jest niezwykle ważne”. Jest cała masa przestarzałych rzeczy i rzeczy, które nigdy nie były dobre, na przykład ustawienia regionalne.
qwr
0

Oprócz obsługi błędów, inną rzeczą, którą możesz zrobić i której wcześniej nie wspomniano, jest inteligentne zaimplementowanie obliczeń rekurencyjnych ogona w języku C.

W ten sposób są implementowane kontynuacje w C bez konwertowania kodu wejściowego w stylu przekazywania kontynuacji.

alinsoar
źródło