C 語言缺乏原生 `try/catch/throw`,傳統錯誤碼層層傳遞不僅代碼冗長,還嚴重干擾主流程可讀性。借助 `
int setjmp(jmp_buf env); // 保存當前棧環境 (寄存器、棧指針等),首次返回 0
void longjmp(jmp_buf env, int val); // 恢復 env 環境,setjmp 再次返回 val (非 0)
關鍵特性:跨函數調用—— `longjmp` 可直接跳回任意上層調用者的 `setjmp` 點,無需逐層 `return`。
每層函數維護自己的 `jmp_buf`,調用下層前先 `memcpy` 保存上層環境,捕獲異常後可選擇:
// main.c
jmp_buf main_stat;
if (setjmp(main_stat) != 0) { printf("error in main\n"); return -1; }
file1();
// file1.c
jmp_buf file1_stat;
memcpy(&file1_stat, &main_stat, sizeof(file1_stat));
if (setjmp(main_stat) != 0) { printf("error in file1\n"); memcpy(&main_stat, &file1_stat, ...); longjmp(main_stat, 1); }
file2();
// file2.c
jmp_buf file2_stat;
memcpy(&file2_stat, &main_stat, sizeof(file2_stat));
if (setjmp(main_stat) != 0) { printf("error in file2\n"); memcpy(&main_stat, &file2_stat, ...); longjmp(main_stat, 1); }
printf("start error\n");
longjmp(main_stat, 1); // 模擬拋出異常
start error
error in file2
error in file1
error in main
異常從最底層 `file2` 依次冒泡至 `main`,每層均有機會記錄日誌、釋放資源、決定是否繼續拋出。
#define TRY do { jmp_buf _buf; if (!setjmp(_buf)) {
#define CATCH } else {
#define FINALLY } } while(0)
// 配合長跳轉宏實現結構化異常處理
雖 `setjmp`/`longjmp` 無法完全替代 C++ 異常機制 (無棧展開、無類型安全),但在純 C 大型項目中,合理封裝後足以構建清晰、高效的分層錯誤處理體系,大幅減少錯誤碼傳遞樣板代碼。