1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > C语言异常处理

C语言异常处理

时间:2021-01-20 04:34:45

相关推荐

C语言异常处理

文章目录

前言一、 异常表达二、 异常报告三、 异常处理

前言

错误与异常:

错误与异常都是在程序编译或者运行时出现的错误, 不同的是,异常可以被开发人员捕捉和处理;而错误,一般不需要开发人员处理(也无法处理),比如内存溢出,如果异常未及时被处理,也可能产生错误。


在开发中,不可避免的需要对异常进行处理,如函数调用时候的异常:

不是指函数设计上的错误而是可以预见的非正常功能的分支

例:

char* strcpy(char* des,const char* source){char* r=des;assert((des != NULL) && (source != NULL));while((*r++ = *source++) != '\0');return des;}


异常处理的意义:

软件开发过程中,大多数时候都在处理异常情况异常不是错误,但是可能导致程序无法正常运行异常处理直接决定软件的鲁棒性和稳定性

一、 异常表达

在C语言中通常通过错误码表示异常,例如Linux中的错误码

优势:错误码定义简单,使用方便

劣势:同一个错误码,在不同程序中意义不仅相同

异常表示的通用设计方法:采用整数分区域的方式对异常进行表示

最高位为符号位,固定为1则所有的错误码都是负数。

err_def.h定义了异常标识和模块标识

#ifndef ERR_DEF_H#define ERR_DEF_H#include <stdio.h>#include <stdint.h>#include <stdbool.h>typedef int32_t err_t;// 模块数量#define MODULE_COUNT 2// 将最高位设为1构成错误码#define ERR_CONSTRUCT(_error_) ((_error_) | (1 << 31))// 根据模块标识确认第一个错误标识的值#define ERR_CODE_BEGIN(_module_) ((_module_) << 16)// 获取模块ID#define ERR_GET_MODULE_ID(_error_) (((_error_) >> 16) & 0x7fff)// 获取错误标识在数组中的下标#define ERR_GET_ERROR_INDEX(_error_) ((_error_) & 0xffff)// 获取错误标识#define ERR_GET_ERROR_CODE(_error_) ((_error_) & 0x7fffffff)typedef enum {TIMER_MODULE,LCD_MODULE} module_enum_t;typedef enum {TIMER_ERR_OK = ERR_CODE_BEGIN(TIMER_MODULE),TIMER_ERR_MEM,TIMER_ERR_BUF,TIMER_ERR_TIMEOUT,TIMER_ERR_VAL} timer_err_enum_t;typedef enum {LCD_ERR_OK = ERR_CODE_BEGIN(LCD_MODULE),LCD_ERR_MEM,LCD_ERR_BUF,LCD_ERR_TIMEOUT,LCD_ERR_VAL} lcd_err_enum_t;#endif

二、 异常报告

通常情况下,系统日志是报告异常的主要形式,但是异常报告并不是处理异常,异常报告只负责记录,而异常处理用于阻止异常程序的崩溃。


异常报告实例:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include "err.h"// 将异常进行输出#define LOG(errno) printf("[%s:%d] Errno: %x\n", __FILE__, __LINE__, errno)err_t lcd_init(){err_t ret = ERR_CONSTRUCT(LCD_ERR_TIMEOUT);return ret;}int main(){err_t ret = lcd_init();if(LCD_ERR_OK != ret){LOG(ret);}}

结果如下,直接打印出错误码,但是可能我们还得自己去查找具体的错误信息。

直接将异常标识和异常打印出来并不友好,不便于定义问题,因此我们希望有更加直观的异常输出方式:

直接将错误码对应的常量名字进行输出枚举常量名包含了模块名和模块内部错误标识

类似下面这样的形式

#ifndef MODULE_H#define MODULE_Htypedef enum {LCD_ERR_OK = ERR_CODE_BEGIN(LCD_MODULE),LCD_ERR_MEM,LCD_ERR_BUF,LCD_ERR_TIMEOUT,LCD_ERR_VAL} lcd_err_enum_t;static const char *lcd_error_str[] = {"LCD_ERR_OK","LCD_ERR_MEM","LCD_ERR_BUF","LCD_ERR_TIMEOUT","LCD_ERR_VAL"}#endif

但是错误标识在开发过程中可能经常发生变动,那么对应的字符数组也要同样变化,在修改过程中非常容易产生错误,需要特别注意,后续将介绍使用自动化代码生成工具,这样只需要定义对应的错误码就可以了,字符串数组自动生成。

工具链接

接下来要做的就是把所有模块的错误标识字符数组关联起来,通过

const char* errno_to_str(err_t errno)转换成为对应的输出:

#include "err.h"static struct error_str_t{// 错误码标识字符数组是否存在bool exist;// 最后一个错误标识值int last_error;// 数组指针const char ** error_array;}s_error_str_array[MODULE_COUNT];// 对应的错误标识字符数组static const char *s_timer_error_str[] = {"TIMER_ERR_OK","TIMER_ERR_MEM","TIMER_ERR_BUF","TIMER_ERR_TIMEOUT","TIMER_ERR_VAL"};static const char *s_lcd_error_str[] = {"LCD_ERR_OK","LCD_ERR_MEM","LCD_ERR_BUF","LCD_ERR_TIMEOUT","LCD_ERR_VAL"};// 初始化 s_error_str_array 数组void error_str_init(){s_error_str_array[TIMER_MODULE].exist = true;s_error_str_array[TIMER_MODULE].last_error = 4;s_error_str_array[TIMER_MODULE].error_array = s_timer_error_str;s_error_str_array[LCD_MODULE].exist = true;s_error_str_array[LCD_MODULE].last_error = 4;s_error_str_array[LCD_MODULE].error_array = s_lcd_error_str;}// 把错误码转换成字符串const char* error_to_str(err_t errno){static bool initialized = false;// 根据错误码获取16位错误标识uint16_t error_code = ERR_GET_ERROR_INDEX(errno);// 根据错误码获取15位模块标识uint16_t module_id = ERR_GET_MODULE_ID(errno);if(!initialized){error_str_init();initialized = true;}if(errno > 0)return "Errno should less than 0";if(!s_error_str_array[module_id].exist)return "Error code array isn't exist";if(s_error_str_array[module_id].last_error < error_code)return "Error code out of range";return s_error_str_array[module_id].error_array[error_code];}

代码中通过模块ID和错误标识找到对应的字符串前需要对s_error_str_array进行初始化(字符数组的生成,初始化,都可以通过自动化完成,但是需要遵循一定的规范)

最后将LOG进行修改,让他可以输出更多信息

// 将异常进行输出#define LOG(errno) do{\const char* str = error_to_str(errno); \printf("[%s:%d] Errno: %s\n", __FILE__, __LINE__, str); \}while(0)

最终的结果,通过对应名字就可以知道错误类型

三、 异常处理

异常处理示例:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include "err.h"// 将异常进行输出#define LOG(errno) do{\const char* str = error_to_str(errno); \printf("[%s:%d] Errno: %s\n", __FILE__, __LINE__, str); \}while(0)err_t lcd_init(){err_t ret = ERR_CONSTRUCT(LCD_ERR_TIMEOUT);return ret;}// 集中进行异常处理bool lcd_error_handle(err_t errno){bool ret = true;err_t err = ERR_GET_ERROR_CODE(errno);switch(err){case LCD_ERR_MEM:break;default:break;}// 如果函数无法处理直接exit结束if(!ret){exit(0);}return ret;}int main(){err_t ret = lcd_init();if(LCD_ERR_OK != ret){LOG(ret);lcd_error_handle(ret);}}

尽量在发生异常的地方报告异常,有助于找到异常发生时候的调用路径。

尽量在上层函数中统一异常,集中处理异常有助于提高代码的可维护性。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。