1. 程式人生 > >異常處理,保證程式碼穩定的必經之步----小話c語言(12)

異常處理,保證程式碼穩定的必經之步----小話c語言(12)

[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 process
void 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 process
void 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 process
void 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 process
void 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