日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

c语言接口作用是什么,C语言接口与实现之异常处理try-except

發(fā)布時(shí)間:2023/12/31 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c语言接口作用是什么,C语言接口与实现之异常处理try-except 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

最近在學(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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。