goto 僅限函數內部exit()/abort() 直接終止程式,無法清理資源try/catch<setjmp.h>)int setjmp(jmp_buf env); // 保存環境,首次返回 0,longjmp 返回時非 0
void longjmp(jmp_buf env, int val); // 恢復環境,使 setjmp 返回 val (val==0 則返回 1)
jmp_buf 為不透明型別,儲存:程式計數器、棧指標、框架指標、暫存器等。
#include <setjmp.h>
#include <stdio.h>
jmp_buf exception_env;
void risky_operation(int code) {
if (code < 0) {
printf("例外觸發: code=%d\n", code);
longjmp(exception_env, code); // 跳回 setjmp
}
printf("正常執行: code=%d\n", code);
}
int main() {
switch (setjmp(exception_env)) {
case 0:
printf("try 區塊開始\n");
risky_operation(1);
risky_operation(-1); // 觸發例外
risky_operation(2); // 不會執行
break;
case -1:
printf("catch 區塊: 捕獲錯誤 -1\n");
break;
default:
printf("catch 區塊: 其他錯誤 %d\n", setjmp(exception_env)); // 注意:這裡需儲存返回值
}
return 0;
}
| 限制 | 說明 | 後果 |
|---|---|---|
| setjmp 所在函數已返回 | 棧幀已銷毀,jmp_buf 失效 | 長跳轉到無效棧 → 崩潰 |
| 變數修改未 volatile | 編譯器最佳化假設變數不變 | longjmp 後變數值舊 |
| C++ 物件解構函數 | longjmp 不呼叫 destructor | 資源洩漏、不變量破壞 |
| 訊號處理函數中使用 | 需 sigsetjmp/siglongjmp | 訊號遮罩未正確恢復 |
#define TRY do { jmp_buf _env; int _err = setjmp(_env); if (!_err) {
#define CATCH(err) } else { err = _err;
#define END_TRY } } while(0)
void foo() {
int err;
TRY {
bar(); // 內部 longjmp(_env, ERR_CODE)
baz();
} CATCH(err) {
fprintf(stderr, "錯誤 %d\n", err);
// 清理資源
} END_TRY;
}
TEST_ASSERT 失敗長跳轉回測試運行器try/catch/throw(自動解構、型別安全)_Noreturn + 錯誤碼傳遞 + 清理函數libexception, cmocka 測試框架內建「setjmp/longjmp 是 C 語言的『核彈』:威力大、副作用強、非不得已勿用。」