Praktische Verwendung von setjmp und longjmp in C

Lesezeit: 16 Minuten

Benutzeravatar von Pala
Pala

Kann mir jemand erklären wo genau setjmp() und longjmp() lassen sich Funktionen in der Embedded-Programmierung sinnvoll einsetzen? Ich weiß, dass diese für die Fehlerbehandlung sind. Aber ich würde gerne einige Anwendungsfälle kennen.

  • Und natürlich, thedailywtf.com/Articles/Longjmp–FOR-SPEED!!!.aspx

    – Daniel Fischer

    4. Februar 2013 um 15:57 Uhr

  • Eine andere Antwort als die gegebenen ist hier stackoverflow.com/questions/7334595/… Sie können verwenden longjmp() um aus einem Signalhandler herauszukommen, insbesondere Dinge wie a BUS ERROR. Dieses Signal kann normalerweise nicht neu gestartet werden. Eine eingebettete Anwendung möchte diesen Fall möglicherweise aus Gründen der Sicherheit und des robusten Betriebs handhaben.

    – ungekünstelter Lärm

    10. März 2013 um 2:45 Uhr

  • Und in Bezug auf Leistungsunterschiede von setjmp zwischen BSD und Linux, siehe “Timing setjmp und die Freude an Standards”was die Verwendung vorschlägt sigsetjmp.

    – 0 _

    12. Januar 2018 um 4:22 Uhr

Curds Benutzeravatar
Quark

Fehlerbehandlung

Angenommen, es gibt einen Fehler tief unten in einer Funktion, die in vielen anderen Funktionen verschachtelt ist, und die Fehlerbehandlung ist nur in der Funktion der obersten Ebene sinnvoll.

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

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

Koroutinen

Neben der Fehlerbehandlung fällt mir auch eine andere Situation ein, in der Sie setjmp/longjmp in C benötigen:

Es ist der Fall, wenn Sie implementieren müssen Koroutinen.

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 Corroutinen unterstützt (soweit ich sehe, basiert es nicht auf nicht standardisiertem oder neuem Verhalten).

BEARBEITEN:

Das kann es tatsächlich sein ist undefiniertes Verhalten zu tun a longjmp Nieder den Callstack (siehe Kommentar von MikeMB; obwohl ich noch keine Gelegenheit hatte, das 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 Ablauf der Ausführung:
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.

  • Da setjmp den Sprung aus dem aktuellen Aufrufbereich zurück in den setjmp-Bereich vorbereitet und longjmp ausführt, wie würde dies die Implementierung von Coroutinen unterstützen? Ich sehe nicht, wie man die Ausführung der Routine fortsetzen könnte, die langjmp´d out war.

    – TheBlastOne

    4. Februar 2013 um 12:08 Uhr

  • @TheBlastOne Siehe der Wikipedia-Artikel. Sie können die Ausführung fortsetzen, wenn Sie setjmp bevor du longjmp. Dies ist nicht standardisiert.

    – Kartoffelklatsche

    4. Februar 2013 um 12:13 Uhr

  • Coroutinen müssen auf separaten Stacks ausgeführt werden, nicht auf demselben wie in Ihrem Beispiel gezeigt. Wie routineA und routineB Verwenden Sie denselben Stack, es funktioniert nur für sehr primitive Coroutinen. Wenn routineA ruft eine tief verschachtelte routineC nach dem ersten Anruf an routineB und das routineC läuft routineB als Koroutine also routineB könnte sogar den Rückgabestapel (nicht nur lokale Variablen) zerstören routineC. Ohne also einen exklusiven Stack (through alloca() nach Anruf rountineB?) werden Sie mit diesem Beispiel ernsthafte Probleme bekommen, wenn es als Rezept verwendet wird.

    – Tine

    11. April 2015 um 19:12 Uhr


  • Bitte erwähnen Sie in Ihrer Antwort, dass das Herunterspringen des Callstacks (von A nach B) ein undefiniertes Verhalten ist).

    – MikeMB

    29. Oktober 2015 um 7:51 Uhr

  • Es ist tatsächlich undefiniert. Sie müssen jede Funktion auf ihrem eigenen unabhängigen Stack ausführen lassen, um den Kontext elegant zu wechseln

    – Neugierig

    26. Dezember 2015 um 18:25 Uhr

Theoretisch können Sie sie zur Fehlerbehandlung verwenden, sodass Sie aus einer tief verschachtelten Aufrufkette herausspringen können, ohne sich mit Behandlungsfehlern in jeder Funktion in der Kette befassen zu müssen.

Wie jede schlaue Theorie zerbricht diese an der Realität. Ihre Zwischenfunktionen weisen Speicher zu, greifen Sperren, öffnen Dateien und erledigen alle möglichen Dinge, die eine Bereinigung erfordern. Also in der Praxis setjmp/longjmp sind normalerweise eine schlechte Idee, außer in sehr begrenzten Fällen, in denen Sie die vollständige Kontrolle über Ihre Umgebung haben (einige eingebettete Plattformen).

Nach meiner Erfahrung in den meisten Fällen, wenn Sie denken, dass die Verwendung setjmp/longjmp funktionieren würde, ist Ihr Programm 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, wie Sie es tun sollten exit wenn der Fehler auftritt.

  • Bitte sieh dir … an libjpeg. Wie in C++ nehmen die meisten Sammlungen von C-Routinen a struct * als Kollektiv an etwas arbeiten. Anstatt die Speicherzuordnungen Ihrer Zwischenfunktionen als Lokale zu speichern, können sie in der Struktur gespeichert werden. Dies ermöglicht a longjmp() Handler, um den Speicher freizugeben. Außerdem hat dies nicht so viele gesprengte Ausnahmetabellen, die alle C++-Compiler 20 Jahre später noch generieren.

    – ungekünstelter Lärm

    10. März 2013 um 2:51 Uhr

  • Like every clever theory this falls apart when meeting reality. Allerdings temporäre Zuordnung und ähnliches vornehmen longjmp()knifflig, da man es dann tun muss setjmp() mehrmals in der Aufrufliste (einmal für jede Funktion, die vor dem Beenden eine Art Bereinigung durchführen muss, die dann “die Ausnahme erneut auslösen” muss, indem longjmp()Bezug auf den Kontext, den es ursprünglich erhalten hatte). Noch schlimmer wird es, wenn diese Ressourcen nachträglich geändert werden setjmp()da Sie sie als deklarieren müssen volatile um die zu verhindern longjmp() davon, sie zu schlagen.

    – Sevko

    12. Juli 2015 um 15:13 Uhr

Benutzeravatar von Meaning-Matters
Sinn-Angelegenheiten

Ich habe a geschrieben Java-ähnlicher Ausnahmebehandlungsmechanismus in C verwenden setjmp(), longjmp() und Systemfunktionen. Es fängt benutzerdefinierte Ausnahmen ab, signalisiert aber auch ähnliches SIGSEGV. Es verfügt über eine unendliche Verschachtelung von Ausnahmebehandlungsblöcken, die über Funktionsaufrufe hinweg funktioniert, und unterstützt die beiden häufigsten Threading-Implementierungen. Es ermöglicht Ihnen, eine Baumhierarchie von Ausnahmeklassen zu definieren, die Link-Time-Vererbung aufweisen, und die catch -Anweisung geht durch diesen Baum, um zu sehen, ob sie fangen oder weitergeben 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 etwas Buchhaltung enthält.

Es war extrem schwierig umzusetzen, das kann ich Ihnen sagen, und ich hätte fast aufgehört. Ich habe wirklich darauf geachtet, es so nah wie möglich an Java zu bringen; Ich fand es überraschend, wie weit ich nur mit C gekommen bin.

Rufen Sie mich an, wenn Sie interessiert sind.

  • Ich bin überrascht, dass dies ohne tatsächliche Compiler-Unterstützung für die benutzerdefinierten Ausnahmen möglich ist. Aber wirklich interessant ist, wie Signale in Ausnahmen umgewandelt werden.

    – Paul Stelian

    13. April 2019 um 20:57 Uhr

  • Ich möchte eines fragen: Was ist mit Ausnahmen, die am Ende nie abgefangen werden? Wie wird main() beendet?

    – Paul Stelian

    13. April 2019 um 20:57 Uhr

  • @PaulStelian Und hier ist deine Antwort auf das Wie main() wird bei nicht abgefangener Ausnahme beendet. Bitte stimmen Sie dieser Antwort zu 🙂

    – Bedeutungsangelegenheiten

    16. April 2019 um 8:16 Uhr


  • @PaulStelian Ah, ich verstehe jetzt, was du meinst. Laufzeitausnahmen, die meiner Meinung nach nicht abgefangen werden, wurden erneut ausgelöst, sodass die allgemeine (plattformabhängige) Antwort gilt. Nicht abgefangene benutzerdefinierte Ausnahmen wurden ausgegeben und ignoriert. Sehen Progagation Abschnitt in der Liesmich 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 Sie denken.

    – Bedeutungsangelegenheiten

    16. April 2019 um 17:29 Uhr


  • Hab mal kurz in die README geschaut, ganz nette da. Im Grunde wird es also an den äußersten try-Block weitergegeben und gemeldet, ähnlich wie bei den asynchronen Funktionen von JavaScript. Nett. Den Quellcode selbst werde ich mir später ansehen.

    – Paul Stelian

    19. April 2019 um 6:51 Uhr

Benutzeravatar von Mats Petersson
Matt Petersson

Die Kombination von setjmp und longjmp ist “Superstärke goto“. Mit ÄUSSERSTER Vorsicht verwenden. Wie andere jedoch erklärt haben, a longjmp ist sehr nützlich, um aus einer unangenehmen Fehlersituation herauszukommen, wenn Sie möchten get me back to the beginning schnell, anstatt eine Fehlermeldung für 18 Funktionsschichten zurücksickern zu lassen.

Allerdings genauso wie goto, aber schlimmer noch, Sie müssen wirklich vorsichtig sein, wie Sie dies verwenden. EIN longjmp bringt Sie nur zurück zum Anfang des Codes. Es wirkt sich nicht auf alle anderen Zustände aus, die sich möglicherweise zwischen dem geändert haben setjmp und wieder wo hin setjmp gestartet. Zuweisungen, Sperren, halb initialisierte Datenstrukturen usw. sind also immer noch zugewiesen, gesperrt und halb initialisiert, wenn Sie dorthin zurückkehren setjmp hieß. Das bedeutet, dass Sie sich wirklich um die Orte kümmern müssen, an denen Sie dies tun, damit es WIRKLICH in Ordnung ist, anzurufen longjmp ohne MEHR Probleme zu verursachen. Natürlich, wenn Sie als nächstes “rebooten” [after storing a message about the error, perhaps] – in einem eingebetteten System, wo Sie beispielsweise festgestellt haben, dass die Hardware in einem schlechten Zustand ist, dann in Ordnung.

habe ich auch gesehen setjmp/longjmp Wird verwendet, 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 dem Aufräumen fertig zu werden”, genauso wie C++ die Ausnahmepunkte im kompilierten Code speichert und dann weiß, was eine Ausnahme ausgelöst hat und was aufgeräumt 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önnten Sie “vollständige Ausnahmebehandlung wie C++” durchführen. Aber es ist ziemlich chaotisch und hängt davon ab, dass der Code gut geschrieben ist.

setjmp und longjmp kann bei Unit-Tests 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 für den Aufruf deklarieren, die nachahmt, was die eigentliche Funktion tut, um bestimmte Flows zu testen. In diesem Fall ruft die Funktion jedoch auf exit der nicht zurückkehrt. Der Stub muss dieses Verhalten irgendwie emulieren. setjmp und longjmp kann das für dich tun.

Um diese Funktion zu testen, können wir folgendes 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 setjmp bevor Sie die zu testende Funktion eingeben, dann in den Stub exit du rufst an longjmp um direkt zu Ihrem Testfall zurückzukehren.

Beachten Sie auch, dass die neu definiert exit verfügt über eine spezielle Variable, die überprüft, ob Sie das Programm tatsächlich beenden möchten, und ruft auf _exit dazu. Wenn Sie dies nicht tun, wird Ihr Testprogramm möglicherweise nicht sauber beendet.

  • @milanHrabos Die done Flag wird auf 0 gesetzt, wenn die Tests ausgeführt werden. Wann exit(2) aufgerufen wird, prüft die Stub-Funktion zuerst, ob done ist 0, was es ist. Dann prüft das die globale should_exit ist 1 (wahr) und die globale expected_code ist 2 (wahr). Dann longjmp wird mit Status 1 aufgerufen. Hierauf wird zurückgesprungen test_div0 woher 1 zurückgegeben wird setjmp.

    – dbusch

    5. Februar 2021 um 15:26 Uhr

Benutzeravatar von Clement J
Clemens J.

Da Sie eingebettet erwähnen, denke ich, dass es erwähnenswert ist, a Nicht-Anwendungsfall: wenn Ihr Codierungsstandard es verbietet. Zum Beispiel MISRA (MISRA-C:2004:Regel 20.7) und JFS (AV-Regel 20): “Das setjmp-Makro und die longjmp-Funktion dürfen nicht verwendet werden.”

  • @milanHrabos Die done Flag wird auf 0 gesetzt, wenn die Tests ausgeführt werden. Wann exit(2) aufgerufen wird, prüft die Stub-Funktion zuerst, ob done ist 0, was es ist. Dann prüft das die globale should_exit ist 1 (wahr) und die globale expected_code ist 2 (wahr). Dann longjmp wird mit Status 1 aufgerufen. Hierauf wird zurückgesprungen test_div0 woher 1 zurückgegeben wird setjmp.

    – dbusch

    5. Februar 2021 um 15:26 Uhr

Zweifellos ist die wichtigste Verwendung von setjmp/longjmp, dass es sich um einen “nicht lokalen Goto-Sprung” handelt. Der Goto-Befehl (und es gibt seltene Fälle, in denen Sie goto über For- und While-Schleifen verwenden müssen) wird am sichersten im selben Bereich verwendet. Wenn Sie goto verwenden, um über Bereiche (oder über die automatische Zuordnung) zu springen, werden Sie höchstwahrscheinlich den Stack Ihres Programms beschädigen. setjmp/longjmp vermeidet dies, indem es die Stapelinformationen an der Stelle speichert, zu der Sie springen möchten. Wenn Sie dann springen, werden diese Stapelinformationen geladen. Ohne diese Funktion müssten sich C-Programmierer höchstwahrscheinlich der Assembler-Programmierung zuwenden, um Probleme zu lösen, die nur setjmp/longjmp lösen könnten. Gott sei Dank existiert es. Alles in der C-Bibliothek ist extrem wichtig. Sie werden wissen, wann Sie es brauchen.

  • “Alles in der C-Bibliothek ist extrem wichtig.” Es gibt eine ganze Reihe veralteter Dinge und Dinge, die nie gut waren, wie z. B. Gebietsschemata.

    – qwr

    7. Juli 2020 um 6:25 Uhr

1423320cookie-checkPraktische Verwendung von setjmp und longjmp in C

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy