Setjmp.h

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

setjmp/longjmp的典型用途是異常處理機制的實現：利用longjmp恢復程序或線程的狀態，甚至可以跳過棧中多層的函數調用。

**成員函數**
setjmp保存當前的環境（即程序的狀態）到平臺相關的一個數據結構 (jmp_buf)，該數據結構在隨後程序執行的某一點可被 longjmp用於恢復程序的狀態到setjmp調用所保存到jmp_buf時的原樣。這一過程可以認為是"跳轉"回setjmp所保存的程序執行狀態。setjmp的返回值指出控制是正常到達該點還是通過調用longjmp恢復到該點。因此有編程的慣用法: if( setjmp(x) ){/* handle longjmp(x) */}。

**成員類型**

**告誡與限制**
longjmp實現了非本地跳轉，微軟的IA32程序設計環境中正常的"棧捲回"("stack unwinding")因而沒有發生，所以諸如棧中已定義的局部變量的析構函數的調用（用於銷燬該局部變量）都沒有執行。所有依賴於棧捲回調用析構函數所做的掃尾工作，如關閉文件、釋放堆內存塊等都沒有做。但在微軟的X64程序設計環境，longjmp啟動了正常的"棧捲回"。
如果setjmp所在的函數已經調用返回了，那麼longjmp使用該處setjmp所填寫的對應jmp_buf緩衝區將不再有效。這是因為longjmp所要返回的"棧幀"(stack frame)已經不再存在了，程序返回到一個不再存在的執行點，很可能覆蓋或者弄壞程序棧.

**使用例子**

**簡單例子**
#include 
#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) )被執行第二次。

**異常處理**
在下例中，setjmp被用於包住一個例外處理，類似try。longjmp調用類似於throw語句，允許一個異常返回給setjmp一個異常值。下屬代碼示例遵從1999 ISO C standard與Single UNIX Specification：僅在特定範圍內引用setjmp
*if，switch或它們的嵌套使用的條件表達式
*上述情況下與!一起使用或者與整數常值比較
*作為單獨的語句(不使用其返回值)
遵從上述規則使得創建程序環境緩衝區更為容易。更一般的使用setjmp可能引起未定義行為，如破壞局部變量；編譯器被要求保護或警告這些用法。但輕微的複雜用法如switch ((exception_type = setjmp(env))) { }在文獻與實踐中是常見的，並保持了相當的可移植性。
#include 
#include 
#include 
#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 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，使得進程知道該次系統調用失敗。

**參考文獻**

**外部鏈接**
  Exceptions in C with Longjmp and Setjmpdate=20110615175107