與刺激的abort函數和exit函數相比,goto語句看起來是處理異常的更可行方案。可是goto是本地的,它只能跳到所在函數內部的標號上,而不能將控制權轉移到所在程序的任意地點(固然,除非你的全部代碼都在main體中)。 爲了解決這個限制,C函數庫提供了setjmp函數和longjmp函數,它們分別承擔非局部標號和goto做用。頭文件申明瞭這些函數及同時所需的jmpbuf數據類型。架構
原理很是簡單:app
1. setjmp(j)設置「jump」點,用正確的程序上下文填充jmpbuf對象j。這個上下文包括程序存放位置、棧和框架指針,其它重要的寄存器和內存數據。當初始化完jump的上下文,setjmp函數返回0值。 框架
2.之後調用longjmp(j,r)的效果就是一個非局部的goto或「長跳轉」到由j描述的上下文處(也就是到那原來設置j的setjmp函數處)。看成爲長跳轉的目標而被調用時,setjmp()返回r或1(若是r設爲0的話)。(記住,setjmp函數不能在這種狀況時返回0。)函數
經過有兩類返回值,setjmp函數讓你知道它正在被怎麼使用。當設置j時,setjmp函數如你指望地執行;但看成爲長跳轉的目標時,setjmp函數就從外面「喚醒」它的上下文。你能夠用longjmp函數來終止異常,用setjmp函數標記相應的異常處理程序。常見的使用方法以下所示。this
#include <setjmp.h> #include <stdio.h> jmp_buf j; void raise_exception(void) { printf("exception raised\n"); longjmp(j, 3); /* jump to exception handler case 3 */ printf("this line should never appear\n"); } int main(void) { switch (setjmp(j)) { case 0: printf("''setjmp'' is initializing ''j''\n"); raise_exception(); printf("this line should never appear\n"); case 1: printf("Case 1\n");break; case 2: printf("Case 2\n");break; case 3: printf("Case 3\n");break; default: break; } return 0; }
最終的輸出結果以下所示。spa
''setjmp'' is initializing ''j'' exception raised Case 3
SylixOS未使用C庫中的setjmp與longjmp函數,而是從新在各個架構中實現。如MIPS架構即是在/libsyllixos/SylixOS/arch/mips/common/mipsLibAsm.S文件中實現這兩個函數,其代碼以下所示。指針
;/************************************************************************** ; sigsetjmp (參數爲 jmp_buf, mask_saved) ;**************************************************************************/ FUNC_DEF(sigsetjmp) CALL_SETUP __sigsetjmpSetup SAVE_NON_VOLATILE_REG JR RA MOV V0 , ZERO FUNC_END(sigsetjmp) ;/************************************************************************** ; longjmp (參數爲 jmp_buf, retval) ;**************************************************************************/ FUNC_DEF(longjmp) CALL_SETUP __longjmpSetup RESTORE_NON_VOLATILE_REG MOV V0 , A1 BNE V0 , ZERO , longjmpnotzero NOP LI V0 , 1 LINE_LABEL(longjmpnotzero) JR RA NOP FUNC_END(longjmp)
《百度百科》code