2009-11-07 7 views
6

tengo unas pocas preguntas relativas a setjmp/longjmp uso -Manejo de excepciones en C - ¿Cuál es el uso de setjmp() que devuelve 0?

  1. ¿Cuál es el uso de setjmp (JMP ___ buf stackVariables) devolver 0. Es un defecto, que no podemos influir.

  2. El único significado de setjmp (stackVariables) es empujar la pila en stackVariables. Y básicamente 0 nos dice si la pila se presionó en stack_variables con éxito.

  3. Su es una ocasión en que el valor es distinto de cero (cualquiera que no sea cero) cuando regrese de un longjmp. ¿Qué devuelve un lomgjmp? ¿Cuándo regresas de longjmp cuando se maneja tu excepción? Esta configuración es realmente confusa.

  4. Pueden algunos relacionarlo para intentar/arrojar y atrapar. Y sería realmente genial, si pudieran proporcionarse algunos buenos ejemplos de setjmp/longjmp.

  5. Es longJmp like throw, y se llama justo después del lugar donde se puede hacer una excepción.

Thanks.

Respuesta

10

La especificación C99 da:

Si la devolución es de una invocación directa, la macro setjmp devuelve el valor cero. Si el retorno proviene de una llamada a la función longjmp, la macro setjmp devuelve un valor distinto de cero .

Así que la respuesta a 1 es que un cero indica que ha llamado setjmp la primera vez, y distinto de cero indica que está regresando de una longjmp.

  1. Empuja el estado actual del programa. Después de una pausa larga, el estado se restablece, el control vuelve al punto en que se llamó y el valor de retorno no es cero.

  2. No hay excepciones en C. Es similar a fork y devuelve diferentes valores dependiendo de si está en el proceso original o de un segundo proceso que ha heredado el entorno, si está familiarizado con eso.

  3. try/catch en C++ llamaremos a destructores en todos los objetos automáticos entre el tiro y el enganche. setjmp/longjmp no llamará a los destructores, ya que no existen en C. Por lo tanto, usted está solo en cuanto a llamar al free en cualquier cosa que tenga malloc ed mientras tanto.

Con esa salvedad, esto:

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

es más o menos equivalente a

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

En el caso C, que tiene que hacer la gestión de memoria explícita (aunque normal sería libre en la función que lo malloc'd antes de llamar a longjmp porque simplifica la vida)

+0

hombre impresionante, gracias –

+0

Es longJmp like throw, y se llama justo después del lugar donde se puede hacer una excepción. Y también por qué está utilizando el número 42, puede hacer el mismo trabajo 1, o es 42 cualquier int positivo distinto de cero. –

+2

El punto clave es que la función setjmp() puede regresar una vez, o puede regresar varias veces. Volverá una vez desde la 'invocación directa' (que devolverá 0); cualquier devolución posterior es el resultado de un longjmp() y devolverá un valor distinto de cero. –

6

setjmp se utiliza para colocar un marcadora donde la llamada de longjump debe devolver, devuelve 0 si es llamado directamente, devuelve 1 si se llama porque un longjmp a que setjmp se invoca.

Tiene que pensar en setjmp como algo que se puede llamar normalmente y no hace nada (devuelve 0) en funcionamiento normal mientras devuelve 1 y se llama indirectamente (y regresa desde allí) cuando se llama un salto largo. Sé lo que quieres decir acerca confuso porque en realidad es confuso ..

Este es el ejemplo dado por Wikipedia:

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

¿Es capaz de entenderlo? Cuando se inicia el programa setjmp se ejecuta en main y devuelve 0 (porque se llama directamente), por lo que se llama first, que llama a second y luego llega longjmp que cambia el contexto que vuelve a setjmp, pero esta vez, desde regresa de un salto y se llama indirectamente la función devuelve 1.

Lo útil del enfoque setjmp/longjmp es que puede manejar situaciones de error sin preocuparse por mantener un indicador entre las llamadas a funciones (especialmente cuando tiene muchas , piense en un procedimiento recursivo para la verificación de tipo en un compilador). Si algo falla en la verificación de tipos en la pila de llamadas, normalmente debe devolver una bandera y continuar devolviéndola para advertir a la persona que llama que la verificación de tipo ha fallado. Con longjmp solo sal y manejas el error sin preocuparte por devolver banderas. El único problema es que esto obliga a un cambio de contexto que no se preocupa por la desasignación estándar de la memoria de pila/montón, por lo que debe manejarlo usted mismo.

+0

gracias por el ejemplo –

+1

El problema que señalar es bastante difícil de manejar, ya que tiene que saber todo lo que se ha asignado antes de la longjmp y cómo desasignar ella.Además, IIRC, cualquier variable local no volátil en la función que emite setjmp puede estar dañada. –

4

La primera parte es casi simple: cuando haces un longjmp, terminas exactamente después del setjmp. Si el valor de retorno es 0, significa que acabas de hacer el setjmp; si no es cero, sabes que llegaste allí desde hace mucho tiempo desde otro lugar. Esa información a menudo es útil para controlar lo que hace tu código después de eso.

setjmp/longjmp son los antiguos antepasados ​​de throw/catch. setjmp/longjmp se definen en C, mientras que throw/catch es el mecanismo más "moderno" para realizar la recuperación de errores en lenguajes más orientados a objetos como C++.

llamada longjmp dice: "Creo que hay algo mal aquí, ayuda, sácame de aquí. Estoy demasiado confundido para limpiarlo después de mí mismo y volver a través de un montón de llamadas funcionales y si, solo llévame de vuelta a donde el mundo estaba bien nuevamente, justo después del último setjmp ".

throw dice más o menos lo mismo, excepto que es más clara y compatible en sintaxis. Además, mientras longjmp puede llevarte prácticamente a CUALQUIER lugar del programa (donde sea que hayas hecho el setjmp), tira termina directamente hacia arriba en la jerarquía de llamadas donde está el lanzamiento.

+0

gracias a la última, 2 párrs. –

0

Solo para añadir a la respuesta (a nd remark) por Pete Kirkham: dado que el estándar C no permite almacenar el valor de retorno de setjmp, quizás el ejemplo de Pete podría cambiarse para usar el cambio en su lugar. Todavía demuestra cómo distinguir entre diferentes valores de retorno pero no infringe el estándar.

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