Setjmp.h

setjmp.h 是 C 標準函式庫中提供 “非本地跳轉” 的標頭檔案：控制流偏離了通常的子程式呼叫與返回序列。互補的兩個函式 setjmp 與 longjmp 提供了這種功能。

setjmp/longjmp 的典型用途是異常處理機制的實現：利用 longjmp 恢復程式或執行緒的狀態，甚至可以跳過棧中多層的函式呼叫。

成員函式

setjmp 儲存當前的環境（即程式的狀態）到平臺相關的一個數據結構 (jmpbuf)，該資料結構在隨後程式執行的某一點可被 longjmp 用於恢復程式的狀態到 setjmp 呼叫所儲存到 jmpbuf 時的原樣。這一過程可以認為是 "跳轉" 回 setjmp 所儲存的程式執行狀態。setjmp 的返回值指出控制是正常到達該點還是透過呼叫 longjmp 恢復到該點。因此有程式設計的慣用法: if (setjmp (x) ){/ handle longjmp (x) /}。

成員型別

告誡與限制

longjmp 實現了非本地跳轉，微軟的 IA32 程式設計環境中正常的 "棧捲回"("stack unwinding") 因而沒有發生，所以諸如棧中已定義的區域性變數的解構函式的呼叫（用於銷燬該區域性變數）都沒有執行。所有依賴於棧捲回呼叫解構函式所做的掃尾工作，如關閉檔案、釋放堆記憶體塊等都沒有做。但在微軟的 X64 程式設計環境，longjmp 啟動了正常的 "棧捲回"。

如果 setjmp 所在的函式已經呼叫返回了，那麼 longjmp 使用該處 setjmp 所填寫的對應 jmp_buf 緩衝區將不再有效。這是因為 longjmp 所要返回的 "棧幀"(stack frame) 已經不再存在了，程式返回到一個不再存在的執行點，很可能覆蓋或者弄壞程式棧.

使用例子

4.1 簡單例子

include

static jmp_buf buf;

void second (void) {printf ("second\n"); // 列印

longjmp (buf,1); // 跳回 setjmp 的呼叫處 - 使得 setjmp 返回值為 1

}

void first (void) {second ;

printf ("first\n"); // 不可能執行到此行

}

int main {if ( ! setjmp (buf) ) {first ; // 進入此行前，setjmp 返回 0

} else { // 當 longjmp 跳轉回，setjmp 返回 1，因此進入此行

printf ("main\n"); // 列印

}

return 0;

}

上述程式將輸出:

second

main

注意到雖然 first 子程式被呼叫，"first" 不可能被列印。"main" 被列印，因為條件語句 if ( ! setjmp (buf) ) 被執行第二次。

4.2 異常處理

在下例中，setjmp 被用於包住一個例外處理，類似 try。longjmp 呼叫類似於 throw 語句，允許一個異常返回給 setjmp 一個異常值。下屬程式碼示例遵從 1999 ISO C standard 與 Single UNIX Specification：僅在特定範圍內引用 setjmp

if，switch 或它們的巢狀使用的條件表示式
上述情況下與！一起使用或者與整數常值比較
作為單獨的語句 (不使用其返回值)

遵從上述規則使得建立程式環境緩衝區更為容易。更一般的使用 setjmp 可能引起未定義行為，如破壞區域性變數；編譯器被要求保護或警告這些用法。但輕微的複雜用法如 switch ((exception_type = setjmp (env))) { } 在文獻與實踐中是常見的，並保持了相當的可移植性。

include

void first (void);

void second (void);

/* This program's output is:

calling first

calling second

entering second

second failed with type 3 exception; remapping to type 1.

first failed, exception type 1

/

/* Use a file scoped static variable for the exception stack so we can access

it anywhere within this translation unit. */

static jmpbuf exceptionenv;

static int exception_type;

int main {

void *volatile mem_buffer;

mem_buffer = NULL;

if (setjmp (exception_env)) {

/ if we get here there was an exception /

printf ("first failed, exception type % d\n", exception_type);

} else {

/ Run code that may signal failure via longjmp. /

printf ("calling first\n");

first ;

mem_buffer = malloc (300); / allocate a resource /

printf ("% s",strcpy ((char) mem_buffer, "first succeeded!")); / ... this will not happen */

}

if (mem_buffer)

free ((void) mem_buffer); / carefully deallocate resource */

return 0;

}

void first (void) {

jmpbuf myenv;

printf ("calling second\n");

memcpy (myenv, exceptionenv, sizeof (jmp_buf));

switch (setjmp (exception_env)) {

case 3:

/ if we get here there was an exception. /

printf ("second failed with type 3 exception; remapping to type 1.\n");

exception_type = 1;

default: / fall through /

memcpy (exceptionenv, myenv, sizeof (jmp_buf)); / restore exception stack /

longjmp (exceptionenv, exceptiontype); / continue handling the exception /

case 0:

/ normal, desired operation /

second ;

printf ("second succeeded\n"); / not reached /

}

memcpy (exceptionenv, myenv, sizeof (jmp_buf)); / restore exception stack /

}

void second (void) {printf ("entering second\n"); / reached /

exception_type = 3;

longjmp (exceptionenv, exceptiontype); / declare that the program has failed /

printf ("leaving second\n"); / not reached /

}

用於訊號處理

在訊號處理機制中，程序在檢查收到的訊號，會從原來的系統呼叫中直接返回，而不是等到該呼叫完成。這種程序突然改變其上下文的情況，就是透過使用 setjmp 和 longjmp 來實現的。setjmp 將儲存的上下文載入使用者空間，並繼續在舊的上下文中繼續執行。這就是說，程序執行一個系統呼叫，當因為資源或其他原因要去睡眠時，核心為程序作了一次 setjmp，如果在睡眠中被訊號喚醒，程序不能再進入睡眠時，核心為程序呼叫 longjmp，該操作是核心為程序將現在的上下文切換成原先透過 setjmp 呼叫儲存在程序使用者區的上下文，這樣就使得程序可以恢復等待資源前的狀態，而且核心為 setjmp 返回 1，使得程序知道該次系統呼叫失敗。