setjmp/longjmp to mechanizm umożliwiający niskopoziomową kontrolę sterowania w języku C bez uciekania się do asemblera. Może być używany np. do implementacji wyjątków.
Użycie Należy wywołać funkcję setjmp z argumentem wskazującym bufor skoku. Funkcja wróci zwracając wartość 0. Następnie jeśli użyjemy funkcji longjmp podając jej jako argumenty bufor skoku i wartość do zwrócenia, sterowanie zostanie przekazane ponownie do miejsca powrotu odpowiadającej funkcji setjmp:
jmp_buf jbuf;
void foo() { if (setjmp(jbuf) != 0)
printf("world!\n"); } else { printf("Hello, "); longjmp(jbuf, 1);
W powyższym przykładzie wydrukowane zostanie Hello, world!\n.
Ponieważ często chcemy skakać pomiędzy funkcjami, bufor jest zwykle zmienną globalną. Typ jmp_buf jest typem wskaźnikowym, więc do funkcji setjmp i longjmp przekazujemy jbuf nie &jbuf.
Bufor skoku staje się nieaktualny po wyjściu z funkcji, z której wywołaliśmy setjmp.
Nagłówki int setjmp(jmp_buf env); void longjmp (jmp_buf env, int val);
Funkcja jest opisana w standardzie POSIX.
Przykład W poniższym przykładzie użyjemy funkcji setjmp i longjmp do implementacji tablic, które zwracają wyjątek w przypadku próby dostępu poza granice tablicy.
#include <setjmp.h> #include <stdio.h> #include <stdlib.h>
jmp_buf array_access_exception; struct array *array_access_exception_base; int array_access_exception_index;
struct array { int *data; int size;
void alloc_array(struct array *array, int size) { array->data = malloc(sizeof(int) * size); array->size = size;
int read_array(struct array *array, int offset) { if (offset < 0 || offset >= array->size)
array_access_exception_base = array; array_access_exception_index = offset; longjmp(array_access_exception, 1);
return array->data[offset];
void write_array(struct array *array, int offset, int value) { if (offset < 0 || offset >= array->size)
array_access_exception_base = array; array_access_exception_index = offset; longjmp(array_access_exception, 2);
array->data[offset] = value;
void copy_array(struct array *a, struct array *b, int cnt) { int i; for(i=0; i<cnt; ++i) write_array(b, i, read_array(a, i));
int main() { struct array a,b; int asz, bsz, cnt; int xretval;
if((xretval = setjmp(array_access_exception)) != 0)
fprintf(stderr, "Trying to %s at invalid offset %i of array %p\n", (xretval == 1) ? "read" : "write", array_access_exception_index, array_access_exception_base); return 1;
printf("Enter size of array a: "); scanf("%d", &asz); printf("Enter size of array b: "); scanf("%d", &bsz); printf("How many elements to copy: "); scanf("%d", &cnt);
alloc_array(&a, asz); alloc_array(&b, bsz); copy_array(&a, &b, cnt);
return 0;
Próba użycia: $ ./arrayexcept Enter size of array a: 15 Enter size of array b: 20 How many elements to copy: 11
$ ./arrayexcept Enter size of array a: 15 Enter size of array b: 20 How many elements to copy: 34 Trying to read at invalid offset 15 of array 0xbffff350
array_access_exception to adres aktualnego handlera wyjątku. array_access_exception_base i array_access_exception_index przechowują dodatkowe informacje zwracane przez wyjątek.
Wszystkie funkcje korzystające z tablic, takie jak copy_array, są o wiele prostsze niż funkcje, które sprawdzałaby jawnie granice tablicy.
Kategoria:Konstrukcje programistyczne