22.2 跳转
除了 if、循环和 goto 这类结构化控制流,C 标准库还提供了非局部跳转能力:setjmp 与 longjmp。它们定义在 <setjmp.h>,可在深层调用栈中快速回到某个恢复点。
1. 基本语义
setjmp(env) 会保存当前执行上下文,并返回 0;后续若调用 longjmp(env, value),程序会跳回该 setjmp 位置,并让 setjmp 的返回值变为 value(若 value 为 0,则返回 1)。
c
#include <setjmp.h>
#include <stdio.h>
static jmp_buf env;
void deep_call(void) {
longjmp(env, 42);
}
int main(void) {
int code = setjmp(env);
if (code == 0) {
deep_call();
} else {
printf("recovered: %d\n", code);
}
return 0;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
输出:
bash
recovered: 42
2. 适用场景
它适合表达“统一失败回收点”这类机制,例如解释器、解析器或受限环境中的错误回退。若普通 return 就能清晰表达控制流,应优先使用普通 return,因为可读性更高。
3. 关键约束
longjmp 只能跳回仍然有效的调用栈上下文;若目标函数已经返回,再跳回去就是未定义行为。另外,setjmp 与自动存储期对象的可见值之间存在细节约束,涉及优化与寄存器分配时尤其要谨慎。
4. 局部对象可见值提示
在 setjmp 返回后,如果某些自动存储期对象在中间路径被修改过,且未使用 volatile,再次通过 longjmp 回到该点时,这些对象的可见值可能不符合直觉。涉及关键状态时,应显式使用更稳妥的状态承载方式,避免依赖编译器优化细节。
5. 与线程模型的边界
jmp_buf 保存的是当前执行上下文,不应跨线程混用。每个线程都应维护自己的恢复点与错误收束路径,避免把一个线程保存的上下文交给另一个线程跳转。把非局部跳转严格限制在线程内部,是确保语义稳定的基础约束。