在看上古代码时发现了setjmp/longjmp函数的使用,由于没见过就查了一下资料,发现这玩意及其花活,特记录一下。
setjmp/longjmp 的设计目的是非局部跳转(non-local goto),主要用于错误恢复和异常处理机制。让我详细讲解它的正确用法和典型场景。
核心设计目的
┌─────────────────────────────────────────┐
│ setjmp/longjmp 设计哲学 │
│ │
│ "在同一条调用栈上进行远距离跳转" │
│ 不是线程/协程切换,而是"超级return" │
└─────────────────────────────────────────┘
典型应用场景
1. 错误恢复(C语言中的"异常处理")
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
jmp_buf error_recovery;
void risky_operation(int x) {
if (x < 0) {
printf("Error: negative value %d\n", x);
longjmp(error_recovery, 1); // 跳转到安全点
}
if (x == 0) {
printf("Error: division by zero risk\n");
longjmp(error_recovery, 2); // 不同错误码
}
printf("Success: processing %d\n", x);
}
int main() {
int error_code = setjmp(error_recovery);
if (error_code == 0) {
// 首次进入,设置跳转点
printf("Starting operations...\n");
risky_operation(10); // 正常
risky_operation(-5); // 触发错误,跳回
} else {
// 从 longjmp 跳回
printf("Recovered from error code: %d\n", error_code);
}
printf("Program continues safely\n");
return 0;
}
输出:
Starting operations...
Success: processing 10
Error: negative value -5
Recovered from error code: 1
Program continues safely
关键点:risky_operation 的栈帧在 longjmp 时被正确销毁,控制流回到 main,栈状态一致。
2. 多层调用栈的回滚
#include <setjmp.h>
#include <stdio.h>
jmp_buf checkpoint;
void level_3() {
printf("In level_3, about to fail\n");
longjmp(checkpoint, 42); // 直接跳回main,跳过level_2和level_1
}
void level_2() {
printf("In level_2\n");
level_3();
printf("This won't print\n");
}
void level_1() {
printf("In level_1\n");
level_2();
printf("This won't print either\n");
}
int main() {
int result = setjmp(checkpoint);
if (result == 0) {
printf("Starting deep call...\n");
level_1();
} else {
printf("Recovered from deep stack with code: %d\n", result);
}
return 0;
}
对比普通return:需要每层都检查返回值并手动传递,代码臃肿。