Setjmp and Longjmp in C
Contents
setjmp and longjmp in C
1. What are setjmp and longjmp?
- setjmp and longjmp are C standard library functions (in `<setjmp.h>`) used for **non-local jumps**—transferring control across function calls.
- Think of them as a way to save a place in the code (setjmp) and jump back to it from somewhere else (longjmp), bypassing the normal stack unwinding.
- Syntax:
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);
jmp_buf
is a type capable of holding all the information needed to restore the program state.
2. Why do we need setjmp and longjmp?
- C does not natively support exceptions (like try-catch in C++/Java).
- In complex C programs, especially embedded or systems code, you sometimes need to escape from deeply nested function calls if an error or special event occurs.
- Alternatives (like return codes) can make the code messy and hard to maintain.
- setjmp/longjmp enable a kind of manual exception handling or error recovery.
3. How does setjmp/longjmp work internally?
setjmp
- Saves the stack context (registers, stack pointer, program counter, maybe more) in a `jmp_buf`.
- Returns 0 when called directly.
- Returns a nonzero value if returning via a longjmp.
longjmp
- Restores the stack context previously saved by setjmp.
- Continues execution as if setjmp just returned, but with the value passed to longjmp.
- Does not unwind/destruct objects like C++ (no destructors or cleanup).
- Can cause issues if misused: resources might leak, or stack may be corrupted if not used carefully.
Visual Flow:
main()
|
|---- foo()
| |
| |---- bar()
| |---- setjmp(jb) <-- saves state here, returns 0
| |---- do something
| |---- longjmp(jb, 42) <-- jumps back to setjmp, returns 42
|
|--- execution resumes in bar(), after setjmp, value==42
4. Use cases, code examples
a) Error Handling Example
#include <stdio.h>
#include <setjmp.h>
jmp_buf env;
void subtask() {
printf("subtask: error occurred!\n");
longjmp(env, 1); // Jump back to saved point in main()
}
int main() {
if (setjmp(env) == 0) {
printf("Normal flow, calling subtask...\n");
subtask();
printf("This won't print\n");
} else {
printf("Recovered from error using longjmp!\n");
}
return 0;
}
Output:
Normal flow, calling subtask...
subtask: error occurred!
Recovered from error using longjmp!
b) Nested Function Example: (simulate try-catch)
#include <stdio.h>
#include <setjmp.h>
jmp_buf jb;
void deep() {
printf("In deep(), something went wrong!\n");
longjmp(jb, 999);
}
void mid() {
printf("In mid(), calling deep...\n");
deep();
printf("This won't print\n");
}
int main() {
int code = setjmp(jb);
if (code == 0) {
printf("Main, calling mid...\n");
mid();
printf("This won't print\n");
} else {
printf("Caught error code: %d\n", code);
}
}
Output:
Main, calling mid...
In mid(), calling deep...
In deep(), something went wrong!
Caught error code: 999
c) Handling Resource Cleanup
- setjmp/longjmp do not call destructors or free resources automatically. You must manually handle resource cleanup!
- Use with caution, especially with allocated memory, file handles, or locks.
5. What can we do more?
- For exception-safe cleanup, prefer goto-based error handling + cleanup labels in C, or use C++ with RAII.
- sigsetjmp/siglongjmp: For POSIX signals, these functions can also save/restore the signal mask (more robust in signal handlers).
- Modern C (since C11): `_Noreturn` attribute for functions that don’t return (like those calling longjmp).
6. Advanced/Other Related Types
sigsetjmp/siglongjmp
- Like setjmp/longjmp but handle signal masks.
- Use when dealing with asynchronous signal handling.
Downsides, Caveats, and Best Practices
- setjmp/longjmp can make code hard to read, debug, and maintain.
- Never use across threads or after the stack frame of setjmp is gone.
- Avoid use with C++ code or any code with non-trivial stack objects (won’t call destructors!).
- Always document their use very clearly in your codebase.
- Use mainly in legacy code, or where no alternatives exist.
7. Summary Table
Feature | setjmp | longjmp |
---|---|---|
Purpose | Save stack context | Restore context/jump |
Return | 0 (direct), | None (never returns) |
>0 (via longjmp) | ||
Typical Use | Error recovery | Error escape/abort |