原文:http://blog.chinaunix.net/uid-10275706-id-3339018.html
起因:在c語言工程代碼當中,常常要處理各種關於異常的處理,每次當程序比較複雜的時候,就必須用好幾個if來嵌套使用,比如
點擊(此處)摺疊或打開
- if (xxx)
- {
- if(xxx)
- {
- }
- else
- {
- return ERR_NO;
- }
- }
這樣的代碼顯得非常混亂,也不容易管理,我一直在尋找能跟c++異常機制類似的功能,如果有這樣的功能,那麼c語言的異常處理不是也很容易打理了麼?
由於c的工程當中一般錯誤都有專有的錯誤列表,所以在這邊,在我們的機制裏我只添加了關於錯誤Id的異常捕捉處理。
思路:
首先,我使用了#define來將c當中沒有的關鍵詞替代,比如try,catch,throw,先想到的是用return,break之類的c語言當中先有的關鍵詞來模擬,這就有了我的第一個版本:
點擊(此處)摺疊或打開
- #ifndef EXCEPTION_H
- #define EXCEPTION_H
- // 一個不成熟的exception機制
- typedef unsigned int ERR_TYPE;
- #define NO_ERR 0
- static ERR_TYPE err;
//try-catch-throw錯誤處理機制
- #define try do
- #define catch(N)
while(0);if(N>=NO_ERR)
- #define throw(N)
err=N;break;
- #define throwAgain(N)
err=N;return;
- #endif
點擊(此處)摺疊或打開
- void f(void)
- {
- try
- {
- throw(6);
- }
- catch(err)
- {
- throwAgain(err);
- }
- }
- int main()
- {
- try
- {
- f();
- }
- catch(err)
- {
- printf("%d",
err);
- }
- }
這個設計的程序有一個很大的問題,那就是它只能在同級的函數下使用try。
這是什麼意思呢?如果我需要這樣使用:
點擊(此處)摺疊或打開
- Int main()
- {
- Try
- {
- f();
- }
- catch(err)
- {
- printf("%d",
err);
- }
- }
- Void f()
- {
- G();
- // some codes follows..
- }
- Void g()
- {
- Throw(3);
- }
在g()throw出異常3的時候,main函數沒有辦法直接捕捉,而是必須在f()函數下面繼續執行後面的語句。就是這套設計的機制多個嵌套的函數是沒有辦法實施的。
那麼還有什麼辦法可以解決這個問題呢?Goto可以嗎?不可以,goto也只能在同級的函數下使用。goto是本地的:它只能跳到所在函數內部的標號上,而不能將控制權轉移到所在程序的任意地點 。
最後找到一個方法:
點擊(此處)摺疊或打開
- #include <setjmp.h>
- int setjmp(jmp_buf env)
- Returns: 0
if called directly, nonzero
if returning from a call
to longjmp.
- void longjmp(jmp_buf env, int val);
這2個函數的使用方法:
點擊(此處)摺疊或打開
- #include<stdio.h>
- #include<setjmp.h>
- jmp_buf ebuf;
- void f2(void);
- int main(void)
- {
- int i;
- printf("1");
- i=setjmp(ebuf);
- if(i==0)
- {
- f2();
- printf("This will not be printed.");
- }
- printf("%d",i);
- return 0;
- }
- void f2(void)
- {
- printf("2");
- longjmp(ebuf,3);
- }
首先,在函數a裏面setjmp,將當前函數的env都存儲起來,然後在函數b裏面調用longjmp,這樣就會直接跳轉到函數a的setjmp這個地方,longjmp的第二個值作用是在第二次返回a函數的時候setjmp會返回longjmp的第二個參數。
有了這個方法,我們的程序就好設計多了,由於設計多個函數的調用,所以設計的時候使用了鏈表來處理保存每個函數的env。
全部實現代碼如下:
點擊(此處)摺疊或打開
- #ifndef LIST_H
- #define LIST_H
- typedef struct list_head_tag
- {
- struct list_head_tag *prev;
- struct list_head_tag *next;
- }list_head;
- #define list_for_each(pos, head)
\
- for
( pos =
(head)->next;
\
- pos !=
(head);
\
- pos = pos->next)
- #define list_for_each_safe(pos,pos_next,head)
\
- for
( pos =
(head)->next, pos_next
= pos->next;
\
- pos !=
(head);
\
- pos = pos_next, pos_next
= pos->next)
- #define list_empty(head)
(head->next
== head)
- #define list_entry(ptr, type, member)
((type*)ptr)
- #define init_list_head(ptr)
\
- do{\
- (ptr)->prev
= ptr;
\
- (ptr)->next
= ptr;
\
- }while(0)
- extern void list_add_before(list_head
*node, list_head
*pos);
- extern void list_add_after(list_head
*node, list_head
*pos);
- extern void list_del(list_head
*node);
- #endif
點擊(此處)摺疊或打開
- #include "list.h"
- void list_add_before(list_head *node, list_head *pos)
- {
- node->prev = pos->prev;
- node->next = pos;
- pos->prev->next = node;
- pos->prev = node;
- }
- void list_add_after(list_head *node, list_head *pos)
- {
- node->prev = pos;
- node->next = pos->next;
- pos->next->prev = node;
- pos->next = node;
- }
- void list_del(list_head *node)
- {
- node->prev->next = node->next;
- node->next->prev = node->prev;
- }
點擊(此處)摺疊或打開
- #ifndef EXC_H
- #define EXC_H
- char err = -1;
- static char isJumpListInit = 0;
- //jmp_buf jump_buffer;
- typedef struct JumpBufListTag
- {
- struct list_head_tag list;
- jmp_buf jump_buffer;
- }JumpBufList, *JumpBufListPtr;
- JumpBufList jumplist = {NULL, NULL};
- JumpBufListPtr head = &jumplist;
- JumpBufListPtr cur = &jumplist;
- int SetCurJump(void)
- {
- JumpBufListPtr newPtr = (JumpBufList*)calloc(sizeof(JumpBufList));
- if (!isJumpListInit)
- {
- init_list_head(&head->list);
- isJumpListInit = 1;
- }
- list_add_after(&newPtr->list, &head->list);
- cur = newPtr;
- return 0;
- }
- void JumpCurLong(void)
- {
- longjmp(cur->jump_buffer, 1);
- }
- void DestoryCurJumpEnv( void )
- {
- list_del(&cur->list);
- free(cur);
- cur = head->list.next;
- }
- #define try SetCurJump();if(setjmp(cur->jump_buffer) == 0)
- #define catch(N) DestoryCurJumpEnv();if(N>=0)
- #define throw(N) err=N;JumpCurLong();
- #endif
使用demo代碼:
點擊(此處)摺疊或打開
- void h()
- {
- throw(7);
- }
- void e()
- {
- h();
- }
- void g(void)
- {
- try
- {
- e();
- printf("g()3");
- }
- catch(err)
- {
- throw(err);
- }
- }
- int main()
- {
-
- try
- {
- g();
- }
- catch(err)
- {
- printf("%d",
err);
- }
- return 0;
- }