setjmp.h
<setjmp.h> 提供非局部跳转功能:setjmp 保存当前执行环境,longjmp 恢复该环境并跳转回 setjmp 处。这是 C 语言实现异常处理、协程等高级控制流的底层机制,但使用复杂且有诸多限制。
基本用法
#include <setjmp.h>
#include <stdio.h>
jmp_buf env;
void second(void)
{
printf("second\n");
longjmp(env, 1); /* 跳转回 setjmp,返回 1 */
}
void first(void)
{
second();
printf("first\n"); /* 不会执行 */
}
int main(void)
{
if (setjmp(env) == 0) { /* 首次执行,返回 0 */
printf("Calling first\n");
first();
} else { /* longjmp 跳转回来 */
printf("Returned from longjmp\n");
}
return 0;
}
/* 输出:
Calling first
second
Returned from longjmp
*/
异常处理模式
#include <setjmp.h>
jmp_buf exception_env;
int exception_code;
#define TRY if (setjmp(exception_env) == 0)
#define CATCH(x) else if (exception_code == (x))
#define THROW(x) do { exception_code = (x); longjmp(exception_env, 1); } while(0)
void risky_function(void)
{
if (error_occurred())
THROW(1);
}
int main(void)
{
TRY {
risky_function();
printf("Success\n");
} CATCH(1) {
printf("Error 1 occurred\n");
}
return 0;
}
限制和陷阱
局部变量值不确定:
jmp_buf env;
int local_var = 0;
if (setjmp(env) == 0) {
local_var = 10;
longjmp(env, 1);
} else {
/* local_var 可能是 0 或 10(取决于优化) */
}
不能跨函数释放资源:
void func(void)
{
FILE *fp = fopen("data.txt", "r");
if (error())
longjmp(env, 1); /* fp 未关闭!泄漏 */
fclose(fp);
}
常见错误
longjmp 到已返回的函数:
jmp_buf env;
void bad(void)
{
jmp_buf local_env;
setjmp(local_env);
/* ... */
longjmp(local_env, 1); /* 错误:函数返回后 local_env 失效 */
}
最佳实践
- 优先用返回值和错误码处理错误
setjmp/longjmp用于复杂恢复场景- 跳转前确保资源已释放
- 跳转后局部变量值不确定
- 现代 C 用
goto清理替代简单场景