Table of Contents

setjmp.h

setjmp.h provides setjmp and longjmp: a mechanism for jumping directly from a function back to an earlier point in the call stack, bypassing all the normal return chain in between. It is the C equivalent of throwing an exception that is caught several frames up — but without any of C++'s object cleanup machinery, which makes it both simpler and more dangerous.

setjmp(env) saves the current register state and stack pointer into env and returns 0. When longjmp(env, val) is called from any function below it on the call stack, control returns to the setjmp site as if it had returned val instead of 0.

#include <setjmp.h>
#include <stdio.h>
 
jmp_buf env;
 
void deep(void) {
    puts("about to jump");
    longjmp(env, 42);
    puts("never reached");
}
 
int main(void) {
    int code = setjmp(env);
    if (code == 0) {
        deep();
    } else {
        printf("landed with code %d\n", code);  // prints 42
    }
    return 0;
}

The main practical use is error recovery in C: a top-level handler calls setjmp; deeply nested code calls longjmp on a fatal error rather than propagating error return codes through every intermediate function. Embedded firmware and parsers sometimes use this pattern.

Two caveats you must know. First, local variables in the function that called setjmp may have their pre-longjmp values if they were held in registers — declare them volatile to be safe. Second, longjmp skips any cleanup code in the unwound frames: free, fclose, pthread_mutex_unlock that would have been called on the normal return path are all bypassed. Leaks and deadlocks are easy to introduce this way.

Practice

// compile: gcc -o jmpdemo jmpdemo.c
// run: ./jmpdemo
// description: simulate error propagation via longjmp without return-code threading
 
#include <setjmp.h>
#include <stdio.h>
 
static jmp_buf err_env;
 
void level3(void) {
    puts("level3: something went wrong");
    longjmp(err_env, 1);
}
 
void level2(void) { level3(); }
void level1(void) { level2(); }
 
int main(void) {
    if (setjmp(err_env) == 0) {
        puts("calling level1...");
        level1();
        puts("never reached");
    } else {
        puts("caught error at top level — cleaned up here");
    }
    return 0;
}

You will see level3 print its message, then control jumps directly to the else branch in main, skipping the level2 and level1 returns entirely. Note that there is no cleanup in level1 or level2 — in real code, anything those functions had opened or allocated would be leaked. That is the trade-off you are making when you use longjmp.