IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言异常处理 -> 正文阅读

[C++知识库]C语言异常处理

前言

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


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

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

例:

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

异常处理的意义:

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

一、 异常表达

在C语言中通常通过错误码表示异常,例如Linux中的错误码
优势:错误码定义简单,使用方便
劣势:同一个错误码,在不同程序中意义不仅相同

ErrnoErrno号码说明
EINTR4系统调用中断。
EAGAIN11资源临时不可用。
EBUSY16资源正忙。
EMFILE24每个进程文件描述符表已满。
EPIPE32管道断开。

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

bit31bit30 - bit16bit15-bit0
1模块标识错误标识

最高位为符号位,固定为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_H

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;

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);
	}
}

尽量在发生异常的地方报告异常,有助于找到异常发生时候的调用路径。
尽量在上层函数中统一异常,集中处理异常有助于提高代码的可维护性。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-11-20 18:12:41  更:2021-11-20 18:12:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:26:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码