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.
Praktische Verwendung von setjmp und longjmp in C
Pala
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:
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 dulongjmp
. 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
undroutineB
Verwenden Sie denselben Stack, es funktioniert nur für sehr primitive Coroutinen. WennroutineA
ruft eine tief verschachtelteroutineC
nach dem ersten Anruf anroutineB
und dasroutineC
läuftroutineB
als Koroutine alsoroutineB
könnte sogar den Rückgabestapel (nicht nur lokale Variablen) zerstörenroutineC
. Ohne also einen exklusiven Stack (throughalloca()
nach AnrufrountineB
?) 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 astruct *
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 alongjmp()
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 vornehmenlongjmp()
knifflig, da man es dann tun musssetjmp()
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, indemlongjmp()
Bezug auf den Kontext, den es ursprünglich erhalten hatte). Noch schlimmer wird es, wenn diese Ressourcen nachträglich geändert werdensetjmp()
da Sie sie als deklarieren müssenvolatile
um die zu verhindernlongjmp()
davon, sie zu schlagen.– Sevko
12. Juli 2015 um 15:13 Uhr
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
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. Wannexit(2)
aufgerufen wird, prüft die Stub-Funktion zuerst, obdone
ist 0, was es ist. Dann prüft das die globaleshould_exit
ist 1 (wahr) und die globaleexpected_code
ist 2 (wahr). Dannlongjmp
wird mit Status 1 aufgerufen. Hierauf wird zurückgesprungentest_div0
woher 1 zurückgegeben wirdsetjmp
.– dbusch
5. Februar 2021 um 15:26 Uhr
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. Wannexit(2)
aufgerufen wird, prüft die Stub-Funktion zuerst, obdone
ist 0, was es ist. Dann prüft das die globaleshould_exit
ist 1 (wahr) und die globaleexpected_code
ist 2 (wahr). Dannlongjmp
wird mit Status 1 aufgerufen. Hierauf wird zurückgesprungentest_div0
woher 1 zurückgegeben wirdsetjmp
.– 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
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 aBUS 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ägtsigsetjmp
.– 0 _
12. Januar 2018 um 4:22 Uhr