首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 語言異常處理機制:setjmp 與 longjmp 實現非局部跳轉與錯誤恢復

     編輯:關於C語言

背景

C 無原生異常處理,常用返回碼檢查,導致代碼冗長。setjmp/longjmp 提供**非局部跳轉**,可跨函數棧幀直接返回錯誤處理點,模擬類似 try/catch 行為。

原理:操縱活動記錄(AR)

  • 每次函數調用在棧上分配 AR(返回地址、局部變量、保存的寄存器、前幀指針)。
  • 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

模擬 try/catch 多異常

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++ 對比

// C++ 异常自动调用析构函数,栈展开安全
try { f1(); f2(); }
catch (Ex1&) { handle1(); }
catch (Ex2&) { handle2(); }

C++11 引入 noexcept、移除動態異常規范,現代 C++ 傾向**錯誤碼/Expected/Outcome** 庫而非異常,但棧展開機制仍是核心優勢。

工程建議

  • 純 C 項目:setjmp/longjmp 可用於**嚴重錯誤快速退出**(如解析器錯誤、初始化失敗),文檔明確標註跳轉點。
  • 避免在正常控制流中濫用,導致「面條代碼」。
  • C++ 項目**嚴禁**使用,改用標準異常或錯誤碼模式。

總結

setjmp/longjmp 通過保存/恢復棧幀上下文實現跨函數非局部跳轉,是 C 語言實現類異常錯誤處理的底層機制。理解其對活動記錄的操作,有助於深入掌握調用約定、棧展開、異常處理實現原理。但需謹記**C++ 不兼容、資源管理風險、線程不安全**等限制,僅在純 C 特定場景謹慎使用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
問題說明
局部對象析構C++ 中 longjmp **不保證**調用已構造對象的析構函數(GCC 不調用,MSVC 可能調用),導致資源洩漏。C++ 禁用 setjmp/longjmp,必須用 throw/catch。
變長數組/Alloca跳躍跨越 VLA 作用域行為未定義。
信號處理程序僅 siglongjmp/sigsetjmp 可在信號處理程序中安全使用。
多線程jmp_buf 非線程安全,需線程局部存儲。