C 無原生異常處理,常用返回碼檢查,導致代碼冗長。setjmp/longjmp 提供**非局部跳轉**,可跨函數棧幀直接返回錯誤處理點,模擬類似 try/catch 行為。
setjmp(jmp_buf env) 保存**當前 AR 關鍵上下文**(棧指針、幀指針、指令指針、寄存器)到 env,返回 0。longjmp(env, val) 恢復 env 中上下文,**跳回 setjmp 調用點之後**,並將 setjmp 返回值強制為 val(非 0)。#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>
jmp_buf env;
void risky() {
printf("in risky\n");
longjmp(env, 1); // 拋出異常代碼 1
printf("never\n");
}
int main() {
if (setjmp(env) == 0) {
risky();
} else {
printf("caught error\n");
}
return 0;
}
輸出:
in risky
caught error
void f1() { if (error1) longjmp(env, 1); }
void f2() { if (error2) longjmp(env, 2); }
int main() {
switch (setjmp(env)) {
case 0: f1(); f2(); break;
case 1: handle1(); break;
case 2: handle2(); break;
}
}
void a() { b(); }
void b() { c(); }
void c() { longjmp(env, 99); } // 直接跳回 main
int main() { if (!setjmp(env)) a(); }
| 問題 | 說明 |
|---|---|
| 局部對象析構 | C++ 中 longjmp **不保證**調用已構造對象的析構函數(GCC 不調用,MSVC 可能調用),導致資源洩漏。C++ 禁用 setjmp/longjmp,必須用 throw/catch。 |
| 變長數組/Alloca | 跳躍跨越 VLA 作用域行為未定義。 |
| 信號處理程序 | 僅 siglongjmp/sigsetjmp 可在信號處理程序中安全使用。 |
| 多線程 | jmp_buf 非線程安全,需線程局部存儲。 |