展示使用 `setjmp` 與 `longjmp` 實作類似 try-catch 的非局部跳轉機制。包含一個捕獲點與兩個拋出點,演示堆疊展開行為。
jmp_buf jmpbuffer:儲存堆疊環境(程式計數器、堆疊指針、暫存器等)setjmp(jmpbuffer):保存當前環境,首次呼叫返回 0longjmp(jmpbuffer, value):跳回 `setjmp` 點,使其返回 value(非 0)#include <iostream>
#include <setjmp.h>
using namespace std;
static jmp_buf jmpbuffer;
static void catch_exception(void);
static void raise_exception_1(void);
static void raise_exception_2(void);
int main(int argc, char *argv[]) {
cout << "main()++" << endl;
catch_exception();
raise_exception_1();
raise_exception_2();
cout << "main()--" << endl;
return 0;
}
static void catch_exception(void) {
cout << "catch_exception()++" << endl;
setjmp(jmpbuffer); // 保存環境,第一次返回 0
cout << "catch_exception()--" << endl; // longjmp 返回後執行此行
}
static void raise_exception_1(void) {
cout << "raise_exception_1()++" << endl;
longjmp(jmpbuffer, 1); // 跳回 catch_exception 的 setjmp 處
cout << "raise_exception_1()--" << endl; // 永遠不會執行
}
static void raise_exception_2(void) {
cout << "raise_exception_2()++" << endl;
longjmp(jmpbuffer, 2); // 再次跳回
cout << "raise_exception_2()--" << endl; // 永遠不會執行
}
輸出: main()++ catch_exception()++ catch_exception()-- raise_exception_1()++ catch_exception()-- raise_exception_2()++ catch_exception()-- main()--
main 呼叫 catch_exceptioncatch_exception 執行 setjmp,儲存環境,返回 0,繼續印出 catch_exception()--main,呼叫 raise_exception_1raise_exception_1 印出 ++,執行 longjmp(jmpbuffer, 1)catch_exception 的 setjmp 行,此次 setjmp 返回 1catch_exception 剩餘程式碼,印出 catch_exception()--main,呼叫 raise_exception_2longjmp(jmpbuffer, 2),setjmp 返回 2,印出 catch_exception()--main,印出 main()--,程式結束longjmp 會直接跳過 中間所有函數的返回流程,不會執行它們的清理程式碼(如解構子、finally 區塊)。這是與 C++ 例外機制的最大差異。
setjmp 首次呼叫返回 0longjmp(buf, val) 跳回時,setjmp 返回 val(非 0)C 標準規定:longjmp 後,非 volatile 的區域變數值為未定義行為。實務上需宣告為 volatile 確保一致性。
| 特性 | setjmp/longjmp | C++ try/catch |
|---|---|---|
| 型別安全 | 無(僅 int 值) | 完整型別系統 |
| 堆疊展開 | 不執行解構子 | 自動呼叫解構子 |
| 跨語言 | C/C++ 通用 | C++ 專用 |
| 效能 | 極快(無 RTTI 開銷) | 較慢(需型別匹配) |
| 標準庫支援 | <setjmp.h> | 語言內建 |
C++17 起可使用 std::optional、std::expected(C++23)或錯誤碼回傳模式,避免非結構化跳轉。C 語言可考慮錯誤碼傳遞或編譯器擴展(如 GCC 的 cleanup 屬性)。
這個範例展示了底層控制流轉移機制,理解它有助於深入掌握執行時期堆疊管理,但在現代 C++ 開發中應謹慎使用。