Setjmp.h

setjmp.h是C標準函式庫中提供“非本地跳轉”的標頭檔案：控制流偏離了通常的子程式呼叫與返回序列。互補的兩個函式setjmp與longjmp提供了這種功能。setjmp/longjmp的典型用途是異常處理機制的實現：利用longjmp恢復程式或執行緒的狀態，甚至可以跳過棧中多層的函式呼叫。成員函式.codice_1儲存當前的環境到平臺相關的一個數據結構 (codice_2)，該資料結構在隨後程式執行的某一點可被 codice_3用於恢復程式的狀態到codice_1呼叫所儲存到codice_2時的原樣。這一過程可以認為是"跳轉"回codice_1所儲存的程式執行狀態。codice_1的返回值指出控制是正常到達該點還是透過呼叫codice_3恢復到該點。因此有程式設計的慣用法: codice_9。告誡與限制.codice_3實現了非本地跳轉，微軟的IA32程式設計環境中正常的"棧捲回"("stack unwinding")因而沒有發生，所以諸如棧中已定義的區域性變數的解構函式的呼叫都沒有執行。所有依賴於棧捲回呼叫解構函式所做的掃尾工作，如關閉檔案、釋放堆記憶體塊等都沒有做。但在微軟的X64程式設計環境，codice_3啟動了正常的"棧捲回"。如果codice_1所在的函式已經呼叫返回了，那麼codice_3使用該處codice_1所填寫的對應codice_2緩衝區將不再有效。這是因為codice_3所要返回的"棧幀"(stack frame)已經不再存在了，程式返回到一個不再存在的執行點，很可能覆蓋或者弄壞程式棧.使用例子.簡單例子.static jmp_buf buf;void second(void) {printf("second\n"); // 列印longjmp(buf,1); // 跳回setjmp的呼叫處 - 使得setjmp返回值為1void first(void) {second();printf("first\n"); // 不可能執行到此行int main() {if ( ! setjmp(buf) ) {first(); // 進入此行前，setjmp返回0} else { // 當longjmp跳轉回，setjmp返回1，因此進入此行printf("main\n"); // 列印return 0;上述程式將輸出:secondmain注意到雖然codice_17子程式被呼叫，"codice_18"不可能被列印。"codice_19"被列印，因為條件語句codice_20被執行第二次。異常處理.在下例中，codice_1被用於包住一個例外處理，類似codice_22。codice_3呼叫類似於codice_24語句，允許一個異常返回給codice_1一個異常值。下屬程式碼示例遵從1999 ISO C standard與Single UNIX Specification：僅在特定範圍內引用codice_1遵從上述規則使得建立程式環境緩衝區更為容易。更一般的使用codice_1可能引起未定義行為，如破壞區域性變數；編譯器被要求保護或警告這些用法。但輕微的複雜用法如codice_31在實踐中是常見的，並保持了相當的可移植性。void first(void);void second(void);/* This program's output is:calling firstcalling secondentering secondsecond 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 jmp_buf exception_env;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) {jmp_buf my_env;printf("calling second\n");memcpy(my_env, exception_env, 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(exception_env, my_env, sizeof(jmp_buf)); /* restore exception stack */longjmp(exception_env, exception_type); /* continue handling the exception */case 0:/* normal, desired operation */second();printf("second succeeded\n"); /* not reached */memcpy(exception_env, my_env, sizeof(jmp_buf)); /* restore exception stack */void second(void) {printf("entering second\n" ); /* reached */exception_type = 3;longjmp(exception_env, exception_type); /* declare that the program has failed */printf("leaving second\n"); /* not reached */用於訊號處理.在訊號處理機制中，程序在檢查收到的訊號，會從原來的系統呼叫中直接返回，而不是等到該呼叫完成。這種程序突然改變其上下文的情況，就是透過使用setjmp和longjmp來實現的。setjmp將儲存的上下文載入使用者空間，並繼續在舊的上下文中繼續執行。這就是說，程序執行一個系統呼叫，當因為資源或其他原因要去睡眠時，核心為程序作了一次setjmp，如果在睡眠中被訊號喚醒，程序不能再進入睡眠時，核心為程序呼叫longjmp，該操作是核心為程序將現在的上下文切換成原先透過setjmp呼叫儲存在程序使用者區的上下文，這樣就使得程序可以恢復等待資源前的狀態，而且核心為setjmp返回1，使得程序知道該次系統呼叫失敗。