Setjmp.hsetjmp.h es un encabezado definido en la biblioteca estándar de C para proporcionar "saltos no locales": control de flujo que se desvía de la llamada a subrutina habitual y la secuencia de retorno. Las funciones complementarias Un uso típico de Funciones miembro
POSIX.1 no especifica si Tipos Miembro
Advertencias y limitacionesCuando un "goto no-local" se ejecuta a través de setjmp / longjmp, el "desenrolle de pila" normal no ocurre y por lo tanto, las acciones de limpieza necesarios, tales como el cierre de descriptores de archivos, el vaciado de los búferes, la liberación de la memoria asignada en el heap, etc., no ocurren. Si la función en la que fue llamada setjmp retorna, ya no es posible utilizar de forma segura longjmp con el objeto jmp_buf correspondiente. Esto es así porque el marco de pila se invalida cuando se devuelve la función. Llamar a longjmp restablece el puntero de pila, que -ya que la función retorna- apuntaría a un marco de pila inexistente y posiblemente sobrescrito o dañado.[3][4] Del mismo modo, C99 no requiere que longjmp preserve el marco de pila actual. Esto significa que saltar a una función que sale a través de una llamada a longjmp no está definido.[5] Sin embargo, la mayoría de las implementaciones de longjmp dejan intacto el marco de pila, lo que permite a setjmp y longjmp ser utilizados para saltar hacia atrás y hacia adelante entre dos o más funciones -una característica explotada para la multitarea-. En comparación con los mecanismos de lenguajes de programación de alto nivel como Python, Java, C + +, C #, e incluso lenguajes pre-C como Algol 60, la técnica de usar setjmp / longjmp para implementar un mecanismo de excepción es engorrosa. Estos lenguajes proporcionan técnicas de manejo de excepciones más potentes, mientras que lenguajes como Scheme, Smalltalk, y Haskell proporcionan construcciones de manejo de continuidad incluso más generales. Ejemplos de usoEjemplo simpleEste ejemplo muestra la idea básica de setjmp. La función main llama a first, la cual a su vez llama a second. La función "second" salta de vuelta a la función main, omitiendo la ejecución de la instrucción printf de la función "first". #include <stdio.h>
#include <setjmp.h>
static jmp_buf buf;
void second(void) {
printf("second\n"); // Impresiones
longjmp(buf,1); // salta de nuevo a donde fue llamado setjmp
}
void first(void) {
second();
printf("first\n"); // No se Imprime
}
int main() {
if ( ! setjmp(buf) ) {
first(); // Cuando se ejecuta, devuelve 0 setjmp
} else { // cuando salta longjmp, setjmp devuelve 1
printf("main\n"); // Impresiones
}
return 0;
}
Manejo de excepciones#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
void first(void);
void second(void);
/* This program's output is:
calling first
calling second
entering second
second failed with type 3 exception; remapping to type 1.
first failed, exception type 1
*/
/* Use a file scoped static variable for the exception stack so we can access
* it anywhere within this translation unit. */
static jmp_buf exception_env;
static int exception_type;
int main() {
void *volatile mem_buffer;
mem_buffer = NULL;
if (setjmp(exception_env)) {
/* if we get here there was an exception */
printf("first failed, exception type %d\n", exception_type);
} else {
/* Run code that may signal failure via longjmp. */
printf("calling first\n");
first();
mem_buffer = malloc(300); /* allocate a resource */
printf(strcpy((char*) mem_buffer, "first succeeded!")); /* ... this will not happen */
}
if (mem_buffer)
free((void*) mem_buffer); /* carefully deallocate resource */
return 0;
}
void first(void) {
jmp_buf my_env;
printf("calling second\n");
memcpy(my_env, exception_env, sizeof(jmp_buf));
switch (setjmp(exception_env)) {
case 3:
/* if we get here there was an exception. */
printf("second failed with type 3 exception; remapping to type 1.\n");
exception_type = 1;
default: /* fall through */
memcpy(exception_env, my_env, sizeof(jmp_buf)); /* restore exception stack */
longjmp(exception_env, exception_type); /* continue handling the exception */
case 0:
/* normal, desired operation */
second();
printf("second succeeded\n"); /* not reached */
}
memcpy(exception_env, my_env, sizeof(jmp_buf)); /* restore exception stack */
}
void second(void) {
printf("entering second\n" ); /* reached */
exception_type = 3;
longjmp(exception_env, exception_type); /* declare that the program has failed */
printf("leaving second\n"); /* not reached */
}
Multitarea cooperativa#include <setjmp.h>
#include <stdio.h>
jmp_buf mainTask, childTask;
void call_with_cushion(void);
void child(void);
int main(void) {
if (!setjmp(mainTask)) {
call_with_cushion(); /* child never returns */ /* yield */
} /* execution resumes after this "}" after first time that child yields */
for (;;) {
printf("Parent\n");
if (!setjmp(mainTask)) {
longjmp(childTask, 1); /* yield - note that this is undefined under C99 */
}
}
}
void call_with_cushion (void) {
char space[1000]; /* Reserve enough space for main to run */
space[999] = 1; /* Do not optimize array out of existence */
child();
}
void child (void) {
for (;;) {
printf("Child loop begin\n");
if (!setjmp(childTask)) longjmp(mainTask, 1); /* yield - invalidates childTask in C99 */
printf("Child loop end\n");
if (!setjmp(childTask)) longjmp(mainTask, 1); /* yield - invalidates childTask in C99 */
}
/* Don't return. Instead we should set a flag to indicate that main()
should stop yielding to us and then longjmp(mainTask, 1) */
}
Referencias
Enlaces externos
|