[Mac 10.7.1 Lion Intel-based x64 gcc4.2.1]
Q: c語言的異常處理可以使用什嗎?
A: 可以使用setjmp和longjmp的組合。一個是儲存處理異常前執行的環境,一個是調回原來執行的環境。
int setjmp(jmp_buf env);
參數env的類型jmp_buf定義如下:
/* * _JBLEN is number of ints required to save the following: * eax, ebx, ecx, edx, edi, esi, ebp, esp, ss, eflags, eip, * cs, de, es, fs, gs == 16 ints * onstack, mask = 2 ints */#define _JBLEN (18)typedef int jmp_buf[_JBLEN];
可以看到jmp_buf是個數群組類型,含有18個int類型資料,包括eax, ebp, esp, eip等環境變數。
它會返回0,便於外部判斷進入異常處理邏輯。
void longjmp(jmp_buf env, int val);
第一個參數為setjmp設定的jmp_buf, 第二個參數為返回異常處理的異常參數,可自訂。
下面是個簡單的例子:
#include <stdio.h>#include <string.h>#include <setjmp.h>#define PRINT_D(intValue) printf(#intValue" is %d\n", (intValue));#define PRINT_STR(str) printf(#str" is %s\n", (str));jmp_buf buf;// exception processvoid exception_process(){ printf("exception process begin...\n"); longjmp(buf, 1); // return to the normal process printf("never execute this...\n"); // so, this line can't be executed}int main(){ int ret; printf("main begin...\n"); ret = setjmp(buf); // save current execute enviroment, go to exception process if(ret) { // returned by exception process printf("pass exception process ...\n"); printf("main end ...\n"); } else { exception_process(); } return 0;}
可以看到setjmp和longjmp因為共同操作了jmp_buf buf;全域變數,所以它們可以在不同函數跳轉並正確返回執行。main函數開始setjmp(buf)一定會返回0, 所以進入exception_process常式,進入後,longjmp(buf, 1);會返回之前執行的地方,進入main的if(ret)邏輯中,執行異常發生後的代碼。
執行結果:
Q: 上面代碼中的buf全域變數可以採用局部變數嗎?
A: 是的,但是需要將buf傳遞給異常處理部分。如下代碼:
#include <stdio.h>#include <string.h>#include <setjmp.h>#define PRINT_D(intValue) printf(#intValue" is %d\n", (intValue));#define PRINT_STR(str) printf(#str" is %s\n", (str));// exception processvoid exception_process(jmp_buf buf){ printf("exception process begin...\n"); longjmp(buf, 1); // return to the normal process printf("never execute this...\n"); // so, this line can't be executed}int main(){ jmp_buf buf; int ret; printf("main begin...\n"); ret = setjmp(buf); // save current execute enviroment, go to exception process if(ret) { // returned by exception process printf("pass exception process ...\n"); printf("main end ...\n"); } else { exception_process(buf); } return 0;}
可以看到exception_process多了一個參數,用於buf的傳遞。
運行結果:
Q: 如果異常原因有幾種,怎麼分辨處理?
A: 這個就需要用到longjmp第二個參數了。
#include <stdio.h>#include <string.h>#include <setjmp.h>#define PRINT_D(intValue) printf(#intValue" is %d\n", (intValue));#define PRINT_STR(str) printf(#str" is %s\n", (str));// input error processvoid exception_input_error_process(jmp_buf buf){ printf("exception input error process begin...\n"); longjmp(buf, 1001); // return to the normal process, 1001 means exception error number}// input too big processvoid exception_input_too_big_process(jmp_buf buf){ printf("exception input too big process begin...\n"); longjmp(buf, 1002); // return to the normal process, 1002 means exception error number}int main(){ jmp_buf buf; int ret, scanf_ret; int n; printf("main begin...\n"); // input n printf("input n:"); scanf_ret = scanf("%d", &n); ret = setjmp(buf); // save current execute enviroment, go to exception process if(ret) { // returned by exception process printf("pass exception process ...\n"); if(ret == 1001) { printf("exception 1001 process end...\n"); } else if(ret == 1002) { printf("exception 1002 process end...\n"); } printf("main end ...\n"); } else { if(scanf_ret < 1) exception_input_error_process(buf); else if(n > 100) exception_input_too_big_process(buf); } return 0;}
如上,如果輸入整形格式不正確,那麼進入輸入錯誤處理;如果輸入的整形超過100,那麼進入輸入過大的處理。
運行結果:
輸入錯誤資料a:
輸入資料為101:
輸入資料50:
此時沒有發生異常。
xichen
2012-5-18 15:18:16