Praktische Verwendung von setjmp und longjmp in C.

96

Kann mir jemand erklären, wo genau setjmp()und welche longjmp()Funktionen in der eingebetteten Programmierung praktisch eingesetzt werden können? Ich weiß, dass diese zur Fehlerbehandlung dienen. Aber ich würde gerne einige Anwendungsfälle kennen.

Pala
quelle
Zur Fehlerbehandlung wie bei jeder anderen Programmierung. Ich sehe keinen Unterschied in der Nutzung ???
Tony der Löwe
3
Und natürlich thedailywtf.com/Articles/Longjmp--FOR-SPEED!!!.aspx
Daniel Fischer
Für Geschwindigkeit? Ja. Weil a) es langsamer läuft als eine Schleife und b) weil es nicht einfach optimiert werden kann (wie das Löschen einer oder zweier Verzögerungen). Also regiere setjmp & longjmp klar!
TheBlastOne
Eine andere Antwort als die angegebenen ist hier stackoverflow.com/questions/7334595/… Sie können verwenden longjmp(), um aus einem Signalhandler auszusteigen, insbesondere Dinge wie a BUS ERROR. Dieses Signal kann normalerweise nicht neu gestartet werden. Eine eingebettete Anwendung möchte diesen Fall möglicherweise aus Sicherheitsgründen und für einen robusten Betrieb behandeln.
Kunstloser Lärm
Und in Bezug auf die Leistungsunterschiede von setjmpzwischen BSD und Linux finden „Timing setjmp und die Freude an der Standards“ , die unter Verwendung schlägt sigsetjmp.
Ioannis Filippidis

Antworten:

81

Fehlerbehandlung
Angenommen , es ist ein Fehler , tief unten in einer Funktion verschachtelt in vielen anderen Funktionen und Fehlerbehandlung macht nur Sinn in der obersten Ebene Funktion.

Es wäre sehr mühsam und umständlich, wenn alle dazwischen liegenden Funktionen normal zurückkehren und Rückgabewerte oder eine globale Fehlervariable auswerten müssten, um festzustellen, dass eine weitere Verarbeitung keinen Sinn ergibt oder sogar schlecht wäre.

In dieser Situation ist setjmp / longjmp sinnvoll. Diese Situationen ähneln Situationen, in denen Ausnahmen in anderen Sprachen (C ++, Java) sinnvoll sind.

Coroutinen
Neben der Fehlerbehandlung kann ich mir auch eine andere Situation vorstellen, in der Sie setjmp / longjmp in C benötigen:

Dies ist der Fall, wenn Sie Coroutinen implementieren müssen .

Hier ist ein kleines Demo-Beispiel. Ich hoffe, es erfüllt die Anfrage von Sivaprasad Palas nach einem Beispielcode und beantwortet die Frage von TheBlastOne, wie setjmp / longjmp die Implementierung von Korroutinen unterstützt (soweit ich sehe, basiert es nicht auf einem nicht standardmäßigen oder neuen Verhalten).

BEARBEITEN:
Es könnte sein, dass es tatsächlich ein undefiniertes Verhalten longjmp ist, den Callstack herunterzufahren (siehe Kommentar von MikeMB; obwohl ich noch keine Gelegenheit hatte, dies zu überprüfen).

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

Die folgende Abbildung zeigt den Ausführungsfluss:
Ablauf der Ausführung


Warnhinweis Beachten Sie bei der Verwendung von setjmp / longjmp, dass sie sich auf die Gültigkeit lokaler Variablen auswirken, die häufig nicht berücksichtigt werden.
Vgl. meine Frage zu diesem Thema .

Quark
quelle
2
Wie würde dies die Implementierung von Coroutinen unterstützen, da setjmp vorbereitet und longjmp den Sprung aus dem aktuellen Aufrufbereich zurück in den setjmp-Bereich ausführt? Ich sehe nicht ein, wie man die Ausführung der Routine fortsetzen könnte, die lange gedauert hat.
TheBlastOne
2
@TheBlastOne Siehe den Wikipedia-Artikel . Sie können die Ausführung fortsetzen, wenn Sie setjmpvor Ihnen longjmp. Dies ist nicht Standard.
Potatoswatter
9
Coroutinen müssen auf separaten Stapeln ausgeführt werden, nicht auf demselben wie in Ihrem Beispiel gezeigt. Da routineAund routineBverwenden Sie den gleichen Stapel, funktioniert es nur für sehr primitive Coroutinen. Wenn routineAAnrufe ein verschachtelt tief routineCauf nach dem ersten Aufruf routineBund dieser routineCläuft routineBals Koroutine, dann routineBkönnte auch die Rückkehr Stack zerstört (nicht nur lokale Variablen) von routineC. Wenn Sie also keinen exklusiven Stapel zuweisen (durch alloca()nach dem Aufruf rountineB?), Werden Sie ernsthafte Probleme mit diesem Beispiel bekommen, wenn es als Rezept verwendet wird.
Tino
6
Bitte erwähnen Sie in Ihrer Antwort, dass das Abspringen des Callstacks (von A nach B) ein undefiniertes Verhalten ist.
MikeMB
1
In der Fußnote 248) heißt es: "Zum Beispiel durch Ausführen einer return-Anweisung oder weil ein anderer longjmp-Aufruf eine Übertragung zu einem setjmp-Aufruf in einer Funktion früher in der Gruppe der verschachtelten Aufrufe verursacht hat." Wenn Sie also eine longjmp-Funktion aus einer Funktion bis zu einem Punkt weiter oben im Callstack aufrufen, wird diese Funktion ebenfalls beendet, und daher ist es UB, danach wieder in sie zurückzuspringen.
MikeMB
18

Die Theorie besagt, dass Sie sie zur Fehlerbehandlung verwenden können, um aus der tief verschachtelten Aufrufkette herauszuspringen, ohne sich mit der Behandlung von Fehlern in jeder Funktion in der Kette befassen zu müssen.

Wie jede kluge Theorie fällt dies auseinander, wenn man der Realität begegnet. Ihre Zwischenfunktionen weisen Speicher zu, greifen zu Sperren, öffnen Dateien und erledigen alle möglichen Dinge, die bereinigt werden müssen. In der Praxis sind setjmp/ longjmpalso normalerweise eine schlechte Idee, außer unter sehr begrenzten Umständen, unter denen Sie die vollständige Kontrolle über Ihre Umgebung haben (einige eingebettete Plattformen).

Nach meiner Erfahrung ist Ihr Programm in den meisten Fällen, wenn Sie glauben, dass die Verwendung von setjmp/ longjmpfunktionieren würde, klar und einfach genug, dass jeder Zwischenfunktionsaufruf in der Aufrufkette eine Fehlerbehandlung durchführen kann, oder es ist so chaotisch und unmöglich zu beheben, dass Sie es tun sollten, exitwenn Sie es tun auf den Fehler stoßen.

Kunst
quelle
3
Bitte schauen Sie libjpeg. Wie in C ++ benötigen die meisten Sammlungen von C-Routinen eine struct *, um etwas als Kollektiv zu bearbeiten. Anstatt die Speicherzuordnungen Ihrer Zwischenfunktionen als Lokale zu speichern, können sie in der Struktur gespeichert werden. Dadurch kann ein longjmp()Handler den Speicher freigeben. Außerdem gibt es hier nicht so viele gestrahlte Ausnahmetabellen, dass alle C ++ - Compiler noch 20 Jahre später generieren.
Kunstloser Lärm
Like every clever theory this falls apart when meeting reality.In der Tat machen temporäre Zuweisungen und dergleichen longjmp()schwierig, da Sie dann setjmp()mehrmals im Aufrufstapel arbeiten müssen (einmal für jede Funktion, die vor dem Beenden eine Art Bereinigung durchführen muss, die dann "die Ausnahme erneut auslösen" muss). durch longjmp()den Kontext, den es ursprünglich erhalten hatte). Es wird noch schlimmer, wenn diese Ressourcen nach dem geändert werden setjmp(), da Sie sie deklarieren müssen volatile, um zu verhindern, dass longjmp()sie überlastet werden.
Sevko
10

Die Kombination von setjmpund longjmpist "Superstärke goto". Mit äußerster Sorgfalt verwenden. Wie andere bereits erklärt haben, ist a longjmpjedoch sehr nützlich, um aus einer unangenehmen Fehlersituation herauszukommen, wenn Sie get me back to the beginningschnell möchten , anstatt eine Fehlermeldung für 18 Funktionsebenen zurückzusenden.

Genauso wie goto, aber schlimmer, müssen Sie WIRKLICH vorsichtig sein, wie Sie dies verwenden. A longjmpbringt Sie nur zurück zum Anfang des Codes. Es wirkt sich nicht auf alle anderen Zustände aus, die sich zwischen dem setjmpund dem Zurückkehren zu dem setjmpAusgangspunkt geändert haben . Zuweisungen, Sperren, halb initialisierte Datenstrukturen usw. werden also weiterhin zugewiesen, gesperrt und halb initialisiert, wenn Sie zu dem Ort zurückkehren, an dem sie setjmpaufgerufen wurden. Dies bedeutet, dass Sie sich wirklich um die Orte kümmern müssen, an denen Sie dies tun, damit es WIRKLICH in Ordnung ist, anzurufen, longjmpohne MEHR Probleme zu verursachen. Wenn Sie als Nächstes "neu starten" [möglicherweise nach dem Speichern einer Meldung über den Fehler] - in einem eingebetteten System, in dem Sie beispielsweise festgestellt haben, dass sich die Hardware in einem schlechten Zustand befindet, ist dies in Ordnung.

Ich habe auch gesehen setjmp/ longjmpverwendet, um sehr grundlegende Threading-Mechanismen bereitzustellen. Aber das ist ein ziemlich spezieller Fall - und definitiv nicht, wie "Standard" -Threads funktionieren.

Bearbeiten: Man könnte natürlich Code hinzufügen, um "mit der Bereinigung umzugehen", genauso wie C ++ die Ausnahmepunkte im kompilierten Code speichert und dann weiß, was eine Ausnahme gegeben hat und was bereinigt werden muss. Dies würde eine Art Funktionszeigertabelle und das Speichern von "Wenn wir hier von unten herausspringen, rufen Sie diese Funktion mit diesem Argument auf" beinhalten. Etwas wie das:

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

Mit diesem System können Sie "die Ausnahmebehandlung wie C ++ abschließen". Aber es ist ziemlich chaotisch und setzt voraus, dass der Code gut geschrieben ist.

Mats Petersson
quelle
+1, natürlich könnten Sie theoretisch eine saubere Ausnahmebehandlung implementieren, indem Sie setjmpjede Initialisierung a la C ++ schützen, und erwähnenswert ist, dass die Verwendung für das Threading nicht Standard ist.
Potatoswatter
8

Da Sie Embedded erwähnen, ist es meines Erachtens erwähnenswert, einen Nichtanwendungsfall zu erwähnen : Wenn Ihr Codierungsstandard dies verbietet. Zum Beispiel MISRA (MISRA-C: 2004: Regel 20.7) und JFS (AV-Regel 20): "Das Makro setjmp und die Funktion longjmp dürfen nicht verwendet werden."

Clement J.
quelle
8

setjmpund longjmpkann beim Testen von Einheiten sehr nützlich sein.

Angenommen, wir möchten das folgende Modul testen:

#include <stdlib.h>

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

Wenn die zu testende Funktion eine andere Funktion aufruft, können Sie normalerweise eine Stub-Funktion zum Aufrufen deklarieren, die nachahmt, was die eigentliche Funktion zum Testen bestimmter Flows tut. In diesem Fall ruft jedoch die Funktion auf, exitdie nicht zurückkehrt. Der Stub muss dieses Verhalten irgendwie emulieren. setjmpund longjmpkann das für dich tun.

Um diese Funktion zu testen, können wir das folgende Testprogramm erstellen:

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

In diesem Beispiel verwenden Sie, setjmpbevor exitSie die longjmpzu testende Funktion eingeben , und rufen dann im Stubbed auf, um direkt zu Ihrem Testfall zurückzukehren.

Beachten Sie auch, dass die neu definierte exitVariable eine spezielle Variable enthält, die überprüft, ob Sie das Programm tatsächlich beenden möchten, und dazu aufruft _exit. Wenn Sie dies nicht tun, wird Ihr Testprogramm möglicherweise nicht sauber beendet.

dbush
quelle
6

Ich habe eine schriftliche Java-ähnliche Ausnahmebehandlung in C unter Verwendung setjmp(), longjmp()und Systemfunktionen. Es fängt benutzerdefinierte Ausnahmen ab, signalisiert aber auch wie SIGSEGV. Es bietet eine unendliche Verschachtelung von Ausnahmebehandlungsblöcken, die über Funktionsaufrufe hinweg funktioniert, und unterstützt die beiden am häufigsten verwendeten Threading-Implementierungen. Sie können eine Baumhierarchie von Ausnahmeklassen definieren, die über eine Vererbung zur Verbindungszeit verfügen, und die catchAnweisung durchsucht diesen Baum, um festzustellen, ob er abgefangen oder weitergegeben werden muss.

Hier ist ein Beispiel dafür, wie Code damit aussieht:

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

Und hier ist ein Teil der Include-Datei, der viel Logik enthält:

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

Es gibt auch ein C-Modul, das die Logik für die Signalverarbeitung und die Buchhaltung enthält.

Die Implementierung war äußerst schwierig. Ich kann Ihnen sagen, und ich habe fast aufgehört. Ich habe mich wirklich bemüht, Java so nahe wie möglich zu kommen. Ich fand es überraschend, wie weit ich mit nur C gekommen bin.

Rufen Sie mich an, wenn Sie interessiert sind.

Sinn-Angelegenheiten
quelle
1
Ich bin überrascht, dass dies ohne tatsächliche Compiler-Unterstützung für die benutzerdefinierten Ausnahmen möglich ist. Aber was wirklich interessant ist, ist, wie Signale in Ausnahmen umgewandelt werden.
Paul Stelian
Ich werde eines fragen: Was ist mit Ausnahmen, die am Ende nie erwischt werden? Wie wird main () beendet?
Paul Stelian
1
@PaulStelian Und hier ist Ihre Antwort darauf , wie main()bei einer nicht erfassten Ausnahme beendet wird. Bitte stimmen Sie dieser Antwort zu :-)
Bedeutung-Angelegenheiten
1
@ PaulStelian Ah, ich verstehe, was du jetzt meinst. Laufzeitausnahmen, die meines Erachtens nicht abgefangen wurden, wurden erneut ausgelöst, damit die allgemeine (plattformabhängige) Antwort gilt. Nicht abgefangene benutzerdefinierte Ausnahmen wurden gedruckt und ignoriert. Siehe ProgagationAbschnitt in der README-Datei. Ich habe meinen Code vom April 1999 auf GitHub gepostet (siehe Link in der bearbeiteten Antwort). Guck mal; Es war eine harte Nuss zu knacken. Wäre schön zu hören, was du denkst.
Bedeutung-Angelegenheiten
2
Habe einen kurzen Blick auf die README geworfen, ziemlich schön dort. Im Grunde genommen wird es also an den äußersten try-Block weitergegeben und gemeldet, ähnlich wie die asynchronen Funktionen von JavaScript. Nett. Ich werde später auf den Quellcode selbst schauen.
Paul Stelian
0

Zweifellos ist die wichtigste Verwendung von setjmp / longjmp, dass es sich um einen "nicht-lokalen Goto-Jump" handelt. Der Befehl Springen (und es gibt seltene Fälle, in denen Sie goto over für und while-Schleifen verwenden müssen) wird im selben Bereich am sichersten verwendet. Wenn Sie mit goto über Bereiche (oder über die automatische Zuweisung) springen, wird der Stapel Ihres Programms höchstwahrscheinlich beschädigt. setjmp / longjmp vermeidet dies, indem die Stapelinformationen an der Stelle gespeichert werden, zu der Sie springen möchten. Wenn Sie dann springen, werden diese Stapelinformationen geladen. Ohne diese Funktion müssten sich C-Programmierer höchstwahrscheinlich an die Assembly-Programmierung wenden, um Probleme zu lösen, die nur mit setjmp / longjmp gelöst werden können. Gott sei Dank existiert es. Alles in der C-Bibliothek ist extrem wichtig. Sie werden wissen, wann Sie es brauchen.

AndreGraveler
quelle
"Alles in der C-Bibliothek ist extrem wichtig." Es gibt eine ganze Reihe von veralteten Sachen und Sachen, die nie gut waren, wie Locales.
qwr