在 C 語言中,我們不能使用 goto 語句來跳轉到另一個函數中的某個 label 處;但提供了兩個函數——setjmp 和 longjmp來完成這種類型的分支跳轉。後面我們會看到這兩個函數在處理異常上面的非常有用。
我們都知道要想在一個函數內進行跳轉,可以使用 goto 語句(不知怎麼該語句在中國學生眼中就是臭名昭著,幾乎所有國內教材都一刀切地教大家盡量不要使用它,但在我看來,這根本不是語言的問題,而是使用該語言的人,看看 Linux 內核中遍地是 goto 語句的應用吧!),但如果從一個函數內跳轉到另一個函數的某處,goto 是不能完成的,那該如何實現呢?
我們要實現的一個 GOTO 語句(我自己定義的),能實現在函數間進行任意跳轉,如下例,在函數 g() 中有條語句GOTO Label;
可以跳轉到 f() 函數的 Label:
標簽所指向的位置,那麼我們該如何實現呢?
void f() { //... Label: //... } void g() { //... GOTO Label; //... }
首先我們要知道,實現這種類型的跳轉,和操作系統中任務切換的上下文切換有點類似,我們只需要恢復 Label 標簽處函數上下文即可。函數的上下文包括以下內容:
這樣,在執行 GOTO Label;
這條語句,我們恢復 Label 處的上下文,即完成跳轉到 Label 處的功能。
如果你讀過 Linux 操作系統進程切換的源碼,你會很明白 Linux 會把進程的上下文保存在 task_struct 結構體中,切換時直接恢復。這裡我們也可以這樣做,將 Label 處的函數上下文保存在某個結構體中,但執行到 GOTO Label 語句時,我們從該結構體中恢復函數的上下文。
這就是函數間進行跳轉的基本原理,而 C 語言中 setjmp 和 longjmp 就為我們完成了這樣的保存上下文和切換上下文的工作。
#include <setjmp.h> int setjmp(jmp_buf env);
setjmp 函數的功能是將函數在此處的上下文保存在 jmp_buf 結構體中,以供 longjmp 從此結構體中恢復。
void longjmp(jmp_buf env, int val);
longjmp 函數的功能是從 jmp_buf 結構體中恢復由 setjmp 函數保存的上下文,該函數不返回,而是從 setjmp 函數中返回。
下面是個簡單的例子,雖然還只是函數內跳轉,但足以說明這兩個函數的功能了。
運行該程序得到的結果為:
i = 0 i = 2
Java、C# 等面向對象語言中都有異常處理的機制,如下就是典型的 Java 中異常處理的代碼,兩個數相除,如果被除數為0拋出異常,在函數 f() 中可以獲取該異常並進行處理:
double divide(double to, double by) throws Bad { if(by == 0) throw new Bad ("Cannot / 0"); return to / by; } void f() { try { divide(2, 0); //... } catch (Bad e) { print(e.getMessage()); } print("done"); }
在 C 語言中雖然沒有類似的異常處理機制,但是我們可以使用 setjmp 和 longjmp 來模擬實現該功能,這也是這兩個函數的一個重要的應用:
static jmp_buf env; double divide(double to, double by) { if(by == 0) longjmp(env, 1); return to / by; } void f() { if (setjmp(env) == 0) divide(2, 0); else printf("Cannot / 0"); printf("done"); }
如果復雜一點,可以根據 longjmp 傳遞的返回值來判斷各種不同的異常,來進行區別的處理,代碼結構如下:
switch(setjmp(env)): case 0: //default //... case 1: //exception 1 //... case 2: //exception 2 //... //...
關於使用 C 語言來處理異常,可以參見這篇文章,介紹了更多復雜的結構,但無外乎就是 setjmp 和 longjmp 的應用。
《C++ 設計新思維》 下載見 http://www.linuxidc.com/Linux/2014-07/104850.htm
C++ Primer Plus 第6版 中文版 清晰有書簽PDF+源代碼 http://www.linuxidc.com/Linux/2014-05/101227.htm
讀C++ Primer 之構造函數陷阱 http://www.linuxidc.com/Linux/2011-08/40176.htm
讀C++ Primer 之智能指針 http://www.linuxidc.com/Linux/2011-08/40177.htm
讀C++ Primer 之句柄類 http://www.linuxidc.com/Linux/2011-08/40175.htm
將C語言梳理一下,分布在以下10個章節中: