1. 异常的概念
(1)程序在运行过程中可能产生异常
(2)异常(Exception)与Bug的区别
①异常是程序运行时可预料的执行分支
②Bug是程序是的错误,是不被预期的运行方式
2. 异常和Bug的对比
(1)异常:如运行时产生除0的情况、需要打开的外部文件不存在、数组访问时越界
(2)Bug:如使用野指针、堆数组使用结束后未释放、选择排序无法处理长度为0的数组
3. C语言经典处理方式:if-else
(1)示例程序
void func(…) { if(判断是否产生异常) { 正常情况代码逻辑 } else { 异常情况代码逻辑 } }
【编程实验】除法操作异常处理
#include <iostream> using namespace std; double divide(double a, double b, int* valid) { const double delta = 0.000000000000001; ; if(!((-delta < b) && (b < delta))) { ret = a / b; *valid = ; //正常 } else { *valid = ; //除0错误 } return ret; } int main() { ; , , &valid); //当第3个参数为NULL时,还是会出问题 if(valid) { cout << "r = " << r << endl; } else { cout << "Divide by zero..." << endl; } ; }
(2)缺陷
①divide函数有3个参数,难以理解其用途
②divide函数调用后必须判断valid代表的结果:true表示结果正常,false表示出现异常
4. C语言异常处理的优化方式
(1)通过setjmp()和longjmp()进行优化
①包含头文件#include <setjmp.h>或<csetjmp>
②int setjmp(jmp_buf env):将当前上下文保存在jum_buf结构体中,以供以后longjmp()恢复状态信息时使用。如果是直接调用setjmp(),那么返回值为0;如果是由于调用longjmp()而调用setjmp(),那么返回值非0。setjmp()只能在某些特定情况下调用,如在if语句、 switch语句及循环语句的条件测试部分以及一些简单的关系表达式中。
③void longjmp(jmp_buf env, int val):用于恢复由最近一次调用setjmp()时保存到env的状态信息。当它执行完时,程序执行流会跳转到setjmp()那行,并根据重新执行setjmp(),但此时的setjmp()得到的返回值是val。
【编程实验】除法操作异常处理优化
#include <iostream> #include <csetjmp> //for setjmp、longjmp(); using namespace std; static jmp_buf env; //须定义全局的上下文环境 double divide(double a, double b) { const double delta = 0.000000000000001; ; if(!((-delta < b) && (b < delta))) { ret = a / b; } else { longjmp(env, ); //当错误发生时会跳转到setjmp那么的代码 //处,重新执行setjmp并把其的返回值设为1 } return ret; } int main() { ) //先保存上下文环境,刚调用时返回值为0 { , ); cout << "r = " << r << endl; } else { cout << "Divide by zero..." << endl; } ; }
(2)缺陷
①引入setjmp()和longjmp()必然涉及到使用全局变量
②暴力跳转导致代码可读性降低
③本质还是if-else异常处理方式
5. C语言异常处理存在的通病
(1)会使程序逻辑中混入大量的处理异常的代码
(2)正常逻辑和异常处理代码混合在一起,导致代码迅速膨胀,难以维护。
【实例分析】异常处理代码分析
#include <iostream> using namespace std; #define SUCCESS 0 #define INVALID_POINTER -1 #define INVALID_LENGTH -2 #define INVALID_PARAMETER -3 int MemSet(void* dest, unsigned int length, unsigned char v) { if(dest == NULL) { return INVALID_POINTER; } ) { return INVALID_LENGTH; } ) || (v > ) ) { return INVALID_PARAMETER; } unsigned char* p = (unsigned char*)dest; ; i< length; i++) { //将char赋值给int。 //1.如果是char,则最高位为符号位,char赋值给int时会扩展最高位 //2.如果是unsigned,则不会扩展,即低8位为char的值,其余24位为0 p[i] = v; } return SUCCESS; } int main() { ]; ); //正常处理与异常处理的代码混杂,不容易看出哪些是正常处理时的代码 //哪些是异常发生时应处理的代码(特别是当返回值用数字表示,而不是这里有意义的常量名时) if(ret == SUCCESS) { } else if(ret == INVALID_POINTER) { } else if(ret == INVALID_LENGTH) { } else if(ret == INVALID_PARAMETER) { } ; }
6. 小结
(1)程序中不可避免的会发生异常
(2)异常是在开发阶段就可以预见的运行时问题
(3)C语言中通过经典的if-else
(4)C++中存在更好的异常处理方式