goto 僅能在函數內部跳轉,若需跨函數跳出(如異常處理、錯誤恢復),C 標準庫提供 setjmp/longjmp 實現非局部轉移。本文詳細講解其機制、用法、陷阱及與 C++ 異常的關係。
#include <setjmp.h> jmp_buf env; // 環境緩衝區,存儲棧幀上下文 int setjmp(jmp_buf env); // 保存當前環境至 env,直接調用返回 0;longjmp 返回時返回非 0 void longjmp(jmp_buf env, int val); // 恢復 env 環境,setjmp 返回 val(val==0 時返回 1)
setjmp(env):保存程序計數器、棧指針、幀指針、通用寄存器等上下文到 env,返回 0 繼續向下執行。longjmp(env, val):恢復 env 中的上下文,程序彈回 setjmp 處,setjmp 返回 val(或 1)。setjmp 時刻,但全局/靜態/堆內存修改保留。#include <stdio.h>
#include <setjmp.h>
jmp_buf buf;
void test() {
printf("進入 test()\n");
longjmp(buf, 1); // 跳回 main 中的 setjmp
printf("返回 test()\n"); // 永不執行
}
int main() {
if (setjmp(buf)) {
printf("main: 通過 longjmp 返回,值非 0\n");
} else {
printf("main: setjmp 首次返回 0\n");
test();
}
return 0;
}
輸出:
main: setjmp 首次返回 0 進入 test() main: 通過 longjmp 返回,值非 0
setjmp 點(即其棟幀未銷毀)。跳往已返回函數的 setjmp 屬未定義行為。volatile 修飾的局部變量在 longjmp 後保持修改值;非 volatile 變量值不確定(編譯器優化可能導致回滾或不回滾)。free),極易導致資源洩漏。longjmp,應改用 try/catch/throw。sigsetjmp/siglongjmp 保存信號掩碼)。C++ 使用異常;C 可設計錯誤碼返回、回調函數、狀態機,或引入 setjmp 封裝的輕量異常庫(如 CEXCEPT),但需嚴格管理資源清理。
setjmp/longjmp 是把雙刃劍:功能強大卻極易濫用。理解其本質是「棧幀時光機」,謹慎使用,並在文檔中明確標註跳轉路徑與資源責任。