7 Stimmen

Ausnahmebehandlung in C - Wozu dient die Funktion setjmp(), die 0 zurückgibt?

Ich habe ein paar Fragen bezüglich der Verwendung von setjmp/longjmp -

  1. Was nützt es, wenn setjmp(jmp___buf stackVariables) 0 zurückgibt. Es ist eine Voreinstellung, die wir nicht beeinflussen können.

  2. Ist die einzige Bedeutung von setjmp(stackVariables), den Stapel in stackVariables zu schieben. Und im Grunde sagt uns 0, ob der Stack erfolgreich auf stack_variables geschoben wurde.

  3. Es gibt einen Fall, in dem der Wert ungleich Null ist (ein beliebiger Wert ungleich Null), wenn Sie von einem longjmp zurückkehren. Was ist die Rückkehr von einem lomgjmp, wann kehren Sie von longjmp, wenn Ihre Ausnahme behandelt wird. Dieser Aufbau ist wirklich verwirrend.

  4. Kann man das bitte auf Versuch/Wurf und Fang beziehen. Und es wäre wirklich toll, wenn einige gute Beispiele für setjmp/longjmp zur Verfügung gestellt werden könnten.

  5. Ist longJmp wie throw und wird direkt nach der Stelle aufgerufen, an der eine Ausnahme ausgelöst werden kann.

Merci.

12voto

Pete Kirkham Punkte 47746

Die C99-Spezifikation gibt an:

Erfolgt die Rückkehr durch einen direkten Aufruf, gibt das Makro setjmp den Wert Null zurück. Wenn die Rückgabe von einem Aufruf der Funktion longjmp stammt, gibt das Makro setjmp einen Wert ungleich Null Wert zurück.

Die Antwort auf die Frage 1 lautet also, dass eine Null anzeigt, dass Sie die setjmp das erste Mal, und ein Wert ungleich Null bedeutet, dass er von einer longjmp .

  1. Es schiebt den aktuellen Programmzustand. Nach einem longjmp wird der Zustand wiederhergestellt, die Kontrolle kehrt zu dem Punkt zurück, an dem sie aufgerufen wurde, und der Rückgabewert ist ungleich Null.

  2. In C gibt es keine Ausnahmen. Es ist ähnlich wie bei fork unterschiedliche Werte zurückgibt, je nachdem, ob man sich im ursprünglichen Prozess oder in einem zweiten Prozess befindet, der die Umgebung geerbt hat, falls Sie damit vertraut sind.

  3. try / catch in C++ ruft Destruktoren für alle automatischen Objekte zwischen dem Wurf und dem Fang auf. setjmp / longjmp ruft keine Destruktoren auf, da es sie in C nicht gibt. Sie sind also auf sich allein gestellt, wenn Sie free über alles, was Sie haben malloc in der Zwischenzeit.

Unter diesem Vorbehalt, dies:

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

void foo ( char** data ) ;
void handle ( char* data ) ;
jmp_buf env;

int main ()
{
    char* data = 0;

    int res = setjmp ( env ); 
    // stored for demo purposes. 
    // in portable code do not store 
    // the result, but test it directly.

    printf ( "setjmp returned %d\n", res );

    if ( res == 0 )
        foo ( &data );
    else
        handle ( data );

    return 0;
}

void foo ( char** data )
{
    *data = malloc ( 32 );

    printf ( "in foo\n" );

    strcpy ( *data, "Hello World" );

    printf ( "data = %s\n", *data );

    longjmp ( env, 42 );
}

void handle ( char* data )
{
    printf ( "in handler\n" );

    if ( data ) {
        free ( data );
        printf ( "data freed\n" );
    }
}

ist ungefähr gleichbedeutend mit

#include <iostream>

void foo ( ) ;
void handle ( ) ;

int main ()
{
    try {
        foo ();
    } catch (int x) {
        std::cout << "caught " << x << "\n";
        handle ();
    }

    return 0;
}

void foo ( )
{
    printf ( "in foo\n" );

    std::string data = "Hello World";

    std::cout << "data = " << data << "\n";

    throw 42;
}

void handle ( )
{
    std::cout << "in handler\n";
}

Im Fall von C müssen Sie den Speicher explizit verwalten (obwohl Sie ihn normalerweise in der Funktion freigeben würden, die ihn vor dem Aufruf von longjmp mallociert hat, da dies das Leben einfacher macht).

7voto

Jack Punkte 128223

Setjmp wird verwendet, um eine Markierung an, wohin der Aufruf von longjump zurückkehren soll, er gibt 0 zurück, wenn er direkt aufgerufen wird, er gibt 1 zurück, wenn er aufgerufen wird, weil ein longjmp zu diesem setjmp aufgerufen wird.

Man muss sich setjmp wie etwas vorstellen, das normalerweise aufgerufen werden kann und im normalen Betrieb nichts tut (0 zurückgibt), während es 1 zurückgibt und indirekt aufgerufen wird (und von dort zurückkehrt), wenn ein Weitsprung aufgerufen wird. Ich weiß, was Sie mit verwirrend meinen, denn es ist tatsächlich verwirrend

Dies ist das Beispiel, das die Wikipedia angibt:

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

static jmp_buf buf;

void second(void)
{
    printf("second\n");         // prints
    longjmp(buf,1);             // jumps back to where setjmp was called - making setjmp now return 1
}

void first(void)
{
    second();
    printf("first\n");          // does not print
}

int main()
{   
    if ( ! setjmp(buf) )
    {
        first();                // when executed, setjmp returns 0
    } 
    else
    {                    // when longjmp jumps back, setjmp returns 1
        printf("main");         // prints
    }

    return 0;
}

Sind Sie in der Lage, sie zu verstehen? Wenn das Programm gestartet wird setjmp wird in main ausgeführt und gibt 0 zurück (weil es direkt aufgerufen wird), also first aufgerufen wird, ruft das second und dann kommt sie an longjmp der den Kontext umschaltet und dorthin zurückkehrt, wo setjmp verwendet, aber dieses Mal gibt die Funktion 1 zurück, da sie von einem Sprung zurückgeht und indirekt aufgerufen wird.

Das Nützliche am setjmp/longjmp-Ansatz ist, dass man mit Fehlersituationen umgehen kann, ohne sich um die Beibehaltung eines Flags zwischen Funktionsaufrufen kümmern zu müssen (vor allem, wenn man viele hat, man denke an eine rekursive Prozedur zur Typüberprüfung in einem Compiler). Wenn bei der Typüberprüfung tief im Aufrufstapel etwas schief geht, muss man normalerweise ein Flag zurückgeben und es immer wieder zurückgeben, um den Aufrufer zu warnen, dass die Typüberprüfung fehlgeschlagen ist. Mit longjmp Du gehst einfach los und behebst Fehler, ohne dich um die Rückgabe von Flaggen zu kümmern. Das einzige Problem ist, dass dies einen Kontextwechsel erzwingt, der sich nicht um die Standard-Deallokation von Stack/Heap-Speicher kümmert, so dass Sie dies selbst erledigen sollten.

6voto

Carl Smotricz Punkte 64366

Der erste Teil ist fast einfach: Wenn Sie einen longjmp ausführen, landen Sie genau nach dem setjmp. Wenn der Rückgabewert 0 ist, bedeutet das, dass Sie gerade den setjmp ausgeführt haben; wenn er ungleich Null ist, wissen Sie, dass Sie durch einen longjmp von woanders dorthin gelangt sind. Diese Information ist oft nützlich, um zu kontrollieren, was Ihr Code danach macht.

setjmp/longjmp sind die alten Vorfahren von throw/catch. setjmp/longjmp sind in C definiert, während throw/catch der "modernere" Mechanismus für die Fehlerbehebung in eher objektorientierten Sprachen wie C++ ist.

Der Aufruf von longjmp sagt: "Ich glaube, hier stimmt etwas nicht, Hilfe, holt mich hier raus - ich bin zu verwirrt, um hinter mir aufzuräumen und durch einen Haufen Funktionsaufrufe und if's zurückzukehren; bringt mich einfach dorthin zurück, wo die Welt wieder in Ordnung war, direkt nach dem letzten setjmp."

throw sagt so ziemlich das Gleiche, nur dass es in der Syntax klarer und sauberer unterstützt ist. Während longjmp Sie praktisch an JEDEN Ort im Programm bringen kann (wo auch immer Sie setjmp ausgeführt haben), landet throw direkt oben in der Aufrufhierarchie, wo sich der throw befindet.

1voto

user2781185 Punkte 333

Nur um die Antwort (und Bemerkung) von Pete Kirkham zu ergänzen: Da der C-Standard das Speichern des Rückgabewerts von setjmp nicht zulässt, könnte Petes Beispiel vielleicht geändert werden, um stattdessen switch zu verwenden. Es demonstriert immer noch, wie man zwischen verschiedenen Rückgabewerten unterscheiden kann, verstößt aber nicht gegen den Standard.

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

void foo(char** data) ;
void handle(char* data) ;
jmp_buf env;

int main(void)
{
  char* data = 0;
  switch(setjmp(env))
  {
    case 0:
    {
      printf("setjmp returned 0\n");
      foo(&data);
      break;
    }
    case 42:
    {
      printf("setjmp returned 42\n");
      handle ( data );
      break;
    }
    default:
    {
      printf("setjmp returned something else?\n");
    }
  }
  return 0;
}

void foo(char** data)
{
  *data = malloc(32);
  printf("in foo\n");
  strcpy(*data, "Hello World");
  printf("data = %s\n", *data);
  longjmp(env, 42);
}

void handle(char* data)
{
  printf("in handler\n");
  if(data)
  {
    free(data);
    printf("data freed\n");
  }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X