#include
int setjmp(jmp_buf env); // 保存當前棧幀(寄存器、棧指針等)到 env,首次返回 0
void longjmp(jmp_buf env, int val); // 恢復 env 棧幀,setjmp 返回 val (非0)
longjmp 可跳回調用鏈上任意已 setjmp 的幀。每層函數維護自己的 jmp_buf,調用下層前保存上層 jmp_buf,捕獲異常後可選擇處理或 longjmp 繼續拋給上層。
/* main.c */
#include
#include
#include
jmp_buf main_jmp;
void file1(void);
void file2(void);
int main() {
if (setjmp(main_jmp) != 0) {
printf("main: caught error\n");
return -1;
}
file1();
return 0;
}
/* file1.c */
#include
#include
extern jmp_buf main_jmp;
jmp_buf file1_jmp;
void file2(void);
void file1() {
memcpy(&file1_jmp, &main_jmp, sizeof(jmp_buf)); // 保存上層環境
if (setjmp(main_jmp) != 0) { // 復用 main_jmp 作為當前層捕獲點
printf("file1: handling / rethrow\n");
memcpy(&main_jmp, &file1_jmp, sizeof(jmp_buf)); // 恢復上層環境
longjmp(main_jmp, 1); // 拋給 main
}
file2();
}
/* file2.c */
#include
#include
extern jmp_buf main_jmp;
jmp_buf file2_jmp;
void file2() {
memcpy(&file2_jmp, &main_jmp, sizeof(jmp_buf));
if (setjmp(main_jmp) != 0) {
printf("file2: handling / rethrow\n");
memcpy(&main_jmp, &file2_jmp, sizeof(jmp_buf));
longjmp(main_jmp, 1);
}
printf("file2: triggering error\n");
longjmp(main_jmp, 1); // 直接拋出
}
file2: triggering error file2: handling / rethrow file1: handling / rethrow main: caught error
memcpy 在層間傳遞 jmp_buf,實現棧幀鏈模擬。1,實際可定義枚舉區分錯誤類型。jmp_buf 為全局/靜態變量時,多線程會衝突。每線程應有獨立異常棧。| 特性 | setjmp/longjmp | C++ try-catch |
|---|---|---|
| 語法支持 | 手動構建 | 關鍵字原生 |
| 棧展開/解構 | 無(需手動) | 自動調用解構函數 |
| 類型安全 | 無(int 錯誤碼) | 任意類型異常對象 |
| 性能(無異常時) | 極低開銷 | 零開銷模型 |
| 可移植性 | C89 標準 | C++ 標準 |
警示:此模式屬「結構化 goto」,濫用會導致控制流難以追蹤。現代 C 項目推薦錯誤碼 + 清理標籤或引入C++ 异常(編譯為 C++ 兼容模式)。