<setjmp.h> defines non-local jumps: save where you are with setjmp, then snap back later with longjmp. It is powerful for deep error recovery (parsers, interpreters) but dangerous for everyday code because it bypasses normal returns and cleanup.
01
setjmp
Save spot.
02
longjmp
Jump back.
03
jmp_buf
Save buffer.
04
val
Return code.
05
volatile
Safe locals.
06
Caution
Not daily use.
Fundamentals
Definition and Usage
A normal function returns with return. setjmp / longjmp let you jump back to an earlier point in the call stack without unwinding each function in between. The saved state lives in a jmp_buf object.
Think of setjmp(env) as planting a bookmark. The first time it runs, the bookmark is placed and the call returns 0. Later, longjmp(env, val) reopens the book at that bookmark; setjmp appears to return again, this time with a non-zero value derived from val.
💡
Beginner Tip
For divide-by-zero or bad user input, use if checks and return codes—not longjmp. This header teaches how C can jump across functions; most apps never need it.
Foundation
📝 Syntax
Include the header:
C
#include <setjmp.h>
Types and macros
C
jmp_buf env; /* buffer holding saved environment */
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);
setjmp and longjmp may be implemented as macros. Do not use env as an expression with side effects, and do not return from the function that called setjmp if longjmp might still target that buffer.
Return rules
setjmp first call → returns 0.
longjmp(env, val) → setjmp returns val, unless val == 0, then it returns 1.
Use distinct non-zero val values to signal different error types.
Critical restrictions
Only volatile local variables are reliable after longjmp.
Do not longjmp to a setjmp in a function that already returned.
Automatic variables in the function that called setjmp may be indeterminate unless volatile.
longjmp does not call fclose, free, etc.—clean up first or leak resources.
Headers and linking
#include <setjmp.h> — no special link flag.
Compile: gcc program.c -std=c11 -o program
Cheat Sheet
⚡ Quick Reference
Call
When
Returns
setjmp(env)
Plant bookmark
0 first time
longjmp(env, n)
Jump to bookmark
(does not return)
setjmp after jump
Resumed
n (or 1 if n==0)
if (setjmp(env)==0)
Normal path
Run protected code
else branch
After longjmp
Error handler
Pattern
if (!setjmp(env)) { work(); }
else { handle(); }
Try / catch style
Signal
longjmp(env, 42)
Custom code
Safe var
volatile int flag;
After jump
Prefer
return -1;
Normal errors
Hands-On
Examples Gallery
Compile with gcc file.c -std=c11 -o out. These examples are for learning control flow—in production, prefer return codes and structured cleanup.
📚 Getting Started
Classic save-and-jump pattern from the reference.
Example 1 — Recover from Division by Zero
From the reference: longjmp unwinds from a nested function to a handler in main.
C
#include <setjmp.h>
#include <stdio.h>
static jmp_buf env;
void divide(int a, int b) {
if (b == 0) {
longjmp(env, 1);
}
printf("Result: %d\n", a / b);
}
int main(void) {
if (setjmp(env) == 0) {
divide(10, 2);
divide(10, 0); /* triggers longjmp */
printf("This line is skipped\n");
} else {
printf("Error: Division by zero!\n");
}
printf("Program continues after recovery\n");
return 0;
}
📤 Output:
Result: 5
Error: Division by zero!
Program continues after recovery
How It Works
setjmp returns 0, so the first branch runs. The second divide calls longjmp(env, 1), which rewinds to setjmp—now it returns 1 and the else branch runs. Code after the failed divide is skipped.
Example 2 — Different longjmp Return Values
Pass error codes through val to distinguish failure reasons.
longjmp(env, 42) makes setjmp return 42. If you passed 0, the standard requires setjmp to return 1 instead—so never use 0 as a meaningful error code.
📈 Practical Patterns
Deep stacks, better alternatives, and volatile locals.
Example 3 — Jump from a Deeply Nested Call
Three levels down—longjmp still reaches main’s setjmp.
C
#include <setjmp.h>
#include <stdio.h>
static jmp_buf env;
void level3(void) {
printf(" level3: about to longjmp\n");
longjmp(env, 99);
}
void level2(void) {
printf(" level2\n");
level3();
printf(" level2: never reached\n");
}
void level1(void) {
printf("level1\n");
level2();
}
int main(void) {
if (setjmp(env) == 0) {
level1();
} else {
printf("Recovered in main (code 99)\n");
}
return 0;
}
📤 Output:
level1
level2
level3: about to longjmp
Recovered in main (code 99)
How It Works
level2 and level3 never return normally. The stack is unwound instantly to the setjmp in main. That power is why resource cleanup is your responsibility.
Example 4 — Prefer Return Codes for Simple Errors
Same divide check without setjmp—clearer for beginners.
C
#include <stdio.h>
int safe_divide(int a, int b, int *out) {
if (b == 0) {
return -1;
}
*out = a / b;
return 0;
}
int main(void) {
int result;
if (safe_divide(10, 2, &result) == 0) {
printf("Result: %d\n", result);
}
if (safe_divide(10, 0, &result) != 0) {
printf("Error: Division by zero!\n");
}
return 0;
}
📤 Output:
Result: 5
Error: Division by zero!
How It Works
Same user-visible result, easier to read and debug. Use this pattern unless you have many nested layers and a compelling reason to jump.
Example 5 — volatile Locals After longjmp
Demonstrates why the standard requires volatile for locals you read after a jump.
C
#include <setjmp.h>
#include <stdio.h>
static jmp_buf env;
int main(void) {
volatile int counter = 0;
int plain = 0;
if (setjmp(env) == 0) {
counter++;
plain++;
longjmp(env, 1);
}
printf("volatile counter = %d (reliable)\n", counter);
printf("plain may be unreliable after longjmp (value: %d)\n", plain);
return 0;
}
📤 Output:
volatile counter = 1 (reliable)
plain may be unreliable after longjmp (value: 0 or 1)
How It Works
counter is volatile, so its value after longjmp is defined. plain might not reflect the increment—behavior is undefined for non-volatile locals in the function that called setjmp.
Applications
🚀 Common Use Cases
Expression parsers — abort parse on syntax error deep in recursion.
Interpreters / VMs — bail out to a top-level REPL on fatal script errors.
Unit test frameworks — some C test runners use jumps for failure recovery.
Legacy codebases — error handling before widespread return-code discipline.
Teaching — understand stack unwinding vs exceptions in other languages.
Not recommended — everyday validation, file I/O, or network errors (use return codes).
🧠 How setjmp.h Works
1
setjmp saves context
Stack pointer, return address, and registers go into jmp_buf.
Bookmark
2
Normal code runs
Functions call each other; an error may occur deep in the stack.
Execute
3
longjmp restores
Context restored; execution resumes as if setjmp just returned val.
Jump
=
💬
Handler runs
The else branch or non-zero setjmp path handles the error.
Important
📝 Notes
setjmp / longjmp are not C++ exceptions—no destructors, no RAII.
Mark locals volatile if their values matter after a jump.
Never longjmp out of a function that has already returned from the setjmp caller.
Free memory and close files beforelongjmp, or you leak resources.
Using setjmp in multiple threads on the same jmp_buf is unsafe.
POSIX offers sigsetjmp / siglongjmp when jumping from signal handlers—see signal.h.
Performance
⚡ Optimization
setjmp saves registers—not free, but usually cheaper than deep manual error propagation in huge recursive parsers. For simple functions, return codes are faster to execute and far easier for the compiler to optimize.
Wrap Up
Conclusion
<setjmp.h> provides non-local jumps via setjmp and longjmp. Plant a bookmark with jmp_buf, jump back with an error code, and handle it in one place—but respect volatile, cleanup, and scope rules.
For most C programs, return values and errno are the right tools. Know setjmp for reading legacy code and specialized parsers—not as your default error handler.
Keep setjmp buffers in scope for the jump lifetime
Prefer return codes for ordinary errors
❌ Don’t
Use longjmp to skip fclose / free
Jump to a setjmp after its function returned
Rely on non-volatile locals after a jump
Replace every if (error) with longjmp
Share one jmp_buf across threads
Summary
Key Takeaways
Knowledge Unlocked
Five things to remember about setjmp.h
Non-local jumps in C, explained simply.
5
Core concepts
💬01
setjmp
Save spot.
Bookmark
📚02
longjmp
Jump back.
Restore
📈03
val
Error code.
Signal
📄04
volatile
Safe vars.
Rules
🌐05
return
Prefer this.
Daily use
❓ Frequently Asked Questions
setjmp.h provides setjmp and longjmp for non-local jumps: save the current execution context in a jmp_buf with setjmp, then later jump back to that point with longjmp—like a time machine for your call stack, but with strict rules.
On the first direct call, setjmp returns 0. When longjmp jumps back, setjmp returns again with the val argument you passed to longjmp—unless val is 0, in which case setjmp returns 1 instead.
Similar idea (jump elsewhere) but different scope. goto only moves within one function. setjmp/longjmp can unwind across function calls—but only to a still-active setjmp in the same call chain. Misuse breaks the stack.
Only volatile locals are guaranteed to keep meaningful values after longjmp. Other locals may be corrupted or stale. Never longjmp past a function that has returned—the jmp_buf becomes invalid.
Usually no. Prefer return codes, errno, and normal cleanup. setjmp/longjmp skip destructors and fclose calls unless you clean up manually. Reserve them for parsers, interpreters, or legacy patterns—not everyday validation errors.
Yes, if setjmp was called in an ancestor that is still active (has not returned). longjmp cannot jump into a setjmp in a function that already returned—that jmp_buf is undefined to use.
Did you know?
Lua’s C API and some Scheme interpreters use setjmp-style mechanisms internally for protected calls. The C standard library itself almost never uses longjmp for you—when malloc fails it returns NULL, and when sqrt(-1) is invalid it returns NaN. That is the style most application code should follow.