c语言接口作用是什么,C语言接口与实现之异常处理try-except
前言
最近在學(xué)習(xí)《C語言接口與實(shí)現(xiàn)》,目前閱讀到第四章,關(guān)于如何實(shí)現(xiàn)C語言異常捕獲和斷言處理,其中的異常捕獲的棧和收尾處理有點(diǎn)不大明白,直到從網(wǎng)上查找到一篇文章才明白棧和結(jié)尾觸發(fā)異常的作用是能夠使得該機(jī)制可以處理多層異常。
在這一章節(jié)中的代碼例程稍顯晦澀難懂,主要是因?yàn)樵摍C(jī)制使用宏來實(shí)現(xiàn),所以語法上并不友好,這就考驗(yàn)到各位的C語言水平了
代碼綜述
在面向?qū)ο蟮恼Z言中,經(jīng)常有異常處理機(jī)制的使用,那么C語言的異常處理機(jī)制按照常規(guī)分為 TRY EXCEPT ELSE FINALLY END_TRY 這5個(gè)部分,下面按照這5個(gè)部分來講。
這里先貼上全部代碼,可見,該機(jī)制是使用setjmp來實(shí)現(xiàn)。這里不講解setjmp和longjmp的用法,請(qǐng)各位自行百度學(xué)習(xí)
typedef struct _Except_t {
char *reason;
} Except_t;
typedef struct Except_Frame Except_Frame;
struct Except_Frame {
Except_Frame *prev;
jmp_buf env;
const char *file;
int line;
const Except_t *exception;
};
enum { Except_entered=0, Except_raised,
Except_handled, Except_finalized };
extern Except_Frame *Except_stack;
extern const Except_t Assert_Failed;
Except_Frame *Except_stack = NULL;
void Except_raise(const Except_t *e, const char *file,
int line) {
Except_Frame *p = Except_stack;
assert(e);
if (p == NULL) {
fprintf(stderr, "Uncaught exception");
if (e->reason)
fprintf(stderr, " %s", e->reason);
else
fprintf(stderr, " at 0x%p", e);
if (file && line > 0)
fprintf(stderr, " raised at %s:%d\n", file, line);
fprintf(stderr, "aborting...\n");
fflush(stderr);
abort();
}
p->exception = e;
p->file = file;
p->line = line;
Except_stack = Except_stack->prev;
longjmp(p->env, Except_raised);
}
void Except_raise(const Except_t *e, const char *file,int line);
#define RAISE(e) Except_raise(&(e), __FILE__, __LINE__)
#define RERAISE Except_raise(Except_frame.exception, \
Except_frame.file, Except_frame.line)
#define RETURN switch (Except_stack = Except_stack->prev,0) default: return
#define TRY do { \
volatile int Except_flag; \
Except_Frame Except_frame; \
Except_frame.prev = Except_stack; \
Except_stack = &Except_frame; \
Except_flag = setjmp(Except_frame.env); \
if (Except_flag == Except_entered) {
#define EXCEPT(e) \
if (Except_flag == Except_entered) Except_stack = Except_stack->prev; \
} else if (Except_frame.exception == &(e)) { \
Except_flag = Except_handled;
#define ELSE \
if (Except_flag == Except_entered) Except_stack = Except_stack->prev; \
} else { \
Except_flag = Except_handled;
#define FINALLY \
if (Except_flag == Except_entered) Except_stack = Except_stack->prev; \
} { \
if (Except_flag == Except_entered) \
Except_flag = Except_finalized;
#define END_TRY \
if (Except_flag == Except_entered) Except_stack = Except_stack->prev; \
} if (Except_flag == Except_raised) RERAISE; \
} while (0);
#endif
TRY
先上代碼
#define TRY do { \
volatile int Except_flag; \
Except_Frame Except_frame; \
Except_frame.prev = Except_stack; \
Except_stack = &Except_frame; \
Except_flag = setjmp(Except_frame.env); \
if (Except_flag == Except_entered) {
TRY 部分很簡單,主要是定義一個(gè)異常幀,并將其壓入棧中,主要用于保存當(dāng)前變量jmp_buf。其實(shí)就是設(shè)置傳送點(diǎn),我們觸發(fā)異常后可以傳送到這里來。這里各位朋友可以想一下,為什么這里需要使用棧,我個(gè)人覺得這里使用棧來保存是一個(gè)很大的妙處。
這個(gè)異常幀在這里是沒有任何信息的,只有在觸發(fā)異常時(shí),觸發(fā)異常函數(shù)Except_raise會(huì)填充其成員,所以只有在返回setjmp時(shí)才會(huì)有異常信息。
如果我們需要其他的異常信息,我們可以在這里寫上我們需要的成員來保存我們想要的異常信息
Except_entered 表示的是進(jìn)入異常捕獲區(qū)域,如果發(fā)生異常,那么Except_flag將會(huì)被改變?yōu)?Except_raised。
EXCEPT
#define EXCEPT(e) \
if (Except_flag == Except_entered) Except_stack = Except_stack->prev; \
} else if (Except_frame.exception == &(e)) { \
Except_flag = Except_handled;
EXCEPT 部分也很簡單,就是判斷Except_flag是否Except_entered,即初始化時(shí)的狀態(tài)。
如果是,則說明沒有發(fā)生異常,并彈出棧頂異常幀。
如果不是,查找所有EXCEPT,如果找到了異常e,則執(zhí)行異常處理函數(shù),并將Except_flag改變?yōu)镋xcept_handled,表明當(dāng)前異常已經(jīng)被處理。
這部分相信不需要過多解釋各位朋友也能明白
ELSE
#define ELSE \
if (Except_flag == Except_entered) Except_stack = Except_stack->prev; \
} else { \
Except_flag = Except_handled;
ELSE也很簡單,同EXCEPT一樣,只是沒有判斷查找異常的過程,這部分相當(dāng)于默認(rèn)分支處理
END_TRY
#define END_TRY \
if (Except_flag == Except_entered) Except_stack = Except_stack->prev; \
} if (Except_flag == Except_raised) RERAISE; \
} while (0);
重點(diǎn)來了,我個(gè)人覺得END_TRY部分是整個(gè)機(jī)制實(shí)現(xiàn)的精髓。
當(dāng)程序執(zhí)行到了這里后有2種判斷情況,一種是沒有發(fā)生異常,直接過。而第二種情況就是
Except_flag 是值為 Except_raised。
這里是為什么會(huì)出現(xiàn) Except_raised呢,不應(yīng)該只有2種情況嗎,一種是被Except_raise發(fā)生并處理異常,最后被修改為Except_handled,一種是程序無異常Except_entered
筆者當(dāng)初有這一點(diǎn)困惑(可能智商不夠用),仔細(xì)翻讀書本也沒找到相關(guān)的敘述。
我在這里解說一下,還有一種情況是如果捕獲為未知異常呢?也就是在使用EXCEPT時(shí)沒有寫出來的異常,那么此時(shí)狀態(tài)就是 Except_raised。
那么什么情況下會(huì)捕獲未知異常呢?
第一、程序員自己寫的異常給忘了,沒有捕獲到該異常,那么此時(shí)程序?qū)?huì)直接退出abort
第二、捕獲異常可以是多層次的,也就是異常捕獲里面再套一層異常捕獲。
我們給出下面的一段代碼
#include
#include "except.h"
Except_t error1 = {"error1"};
Except_t error2 = {"error2"};
Except_t error3 = {"error3"};
Except_t error4 = {"error4"};
Except_t error5 = {"error5"};
Except_t error6 = {"error6"};
Except_t error7 = {"error7"};
int main()
{
TRY//第一層TRY
printf("first try\n");
TRY//第二層TRY
printf("second try\n");
RAISE(error1);
EXCEPT(error5)
printf("catch error5\n");
END_TRY//第二層END_TRY
EXCEPT(error1)
printf("catch error1\n");
EXCEPT(error2)
printf("catch error2\n");
EXCEPT(error3)
printf("catch error3\n");
ELSE
printf("catch default error\n");
END_TRY//第一層END_TRY
}
可以看出我們使用 2 次TRY,那么這個(gè)時(shí)候棧的作用就體現(xiàn)出來了,每次都會(huì)壓入最外層的異常幀。這里我們壓入了2個(gè)異常幀。
那么在代碼中,第二層的TRY觸發(fā)了第一層的error1,此時(shí)代碼的工作流程是這樣的。
拋出異常后,代碼先檢查第二層的所有EXCEPT,發(fā)現(xiàn)沒有處理error1的,那么此時(shí)在第二層的END_TRY中棧里面的棧頂異常幀將會(huì)被彈出。而此時(shí)因?yàn)闆]有經(jīng)過任何EXCEPT處理,那么第二層的Except_flag的值就是Except_raised,所以此時(shí)會(huì)觸發(fā)RERAISE(其實(shí)就是Except_raise)。
在Except_raise中,又會(huì)去取出棧頂?shù)漠惓⑹褂胠ongjmp返回到第一層的TRY,在這里又會(huì)繼續(xù)去查找所有的EXCEPT,直到找到了error1。
到了這里,處理結(jié)束,總結(jié)來說就是:
棧的作用壓入每一層的異常幀,而END_TRY中的Except_raised作用是再次觸發(fā)更上一層的異常,直到將所有的異常幀彈出
好了,以上大致就是本文章的所有內(nèi)容了。當(dāng)然了,這個(gè)異常處理機(jī)制并非完美的,比如在EXCEPT再次拋出異常,那么程序?qū)?huì)直接dump掉,又例如在多線程中,一個(gè)棧會(huì)被壓入不同線程的異常幀。當(dāng)然改進(jìn)的辦法總是有的,在于各位如何實(shí)現(xiàn)而已,這里就不多說了。
那么在linux中實(shí)現(xiàn)當(dāng)然是沒有問題的,在嵌入式平臺(tái)中呢?其實(shí)有些嵌入式平臺(tái)的庫是支持setjmp和longjmp的,只要能夠支持這2個(gè)接口,那么就完全可以實(shí)現(xiàn)該機(jī)制了
后記
筆者以前翻閱過一遍APUE(現(xiàn)在忘得差不多了),當(dāng)時(shí)僅僅是為了加強(qiáng)對(duì)Linux編程的理解,那時(shí)看到setjmp和longjmp并不知道他們的作用。直到閱讀了《C語言接口與實(shí)現(xiàn)》,才知道它們能夠?qū)崿F(xiàn)異常處理機(jī)制。所以很慚愧,這些都是很古老的知識(shí)了,但是我知道現(xiàn)在才知道他們的作用,感慨感慨,果然需要多多學(xué)習(xí)才是。
總結(jié)
以上是生活随笔為你收集整理的c语言接口作用是什么,C语言接口与实现之异常处理try-except的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: arch linux 安装 arm,给树
- 下一篇: stm8s跳出中断程序c语言,stm8s