1、语言本身没有异常处理的原则。


一、异常的概念:


1、程序在运行过程中可能产生异常。


2、异常(Exception)与Bug的区别


@1:异常是程序运行时可预料的执行分支。是我们在程序开发时要考虑的一些特殊情况

@2:Bug是程序中的错误,是不可被预期的运行方式




二、异常(Exception)和Bug的对比


1、异常的例子:


(1)运行时产生除0的情况


(2)需要打开的外部文件不存在


(3)数组访问越界


2、bug的例子:


(1)使用了野指针


(2)堆数组使用结束后未释放


(3)选择排序无法处理长度为0的数组



三、C语言经典处理异常的方式:if...else...


1、

void func(...)

{

if (判断是否产生异常)

{

正常情况的代码逻辑;

}

else

{

异常情况代码逻辑;

}

}


四、编程实践一下异常处理


1、在C语言中,除法操作异常处理(除数为0的情况),不是完美的方法


例:


#include <stdio.h>


/*

* C语言中对除数为0情况下的异常处理方法,不是完美的方法

*

*/


static double divide(double a, double b, int *valid) //用valid表示异常是否发生,1为没发生、0为发生

{

const double delta = 0.000000000001;

double ret = 0;

if (!((-b < delta) && (b < delta)))

{

*valid = 1;

ret = a / b;

}

else

{

*valid = 0;

}

return ret;

}



inline static int char_to_num(const char c)

{

return (c - 48);

}


int main(int argc, char *argv[])

{

int valid = 0;

double r = 0;


#if 0

if (2 == argc)

{

printf("argc = %d. argv[1][0] = %d.\n", argc, argv[1][0]);

}

#endif

r = divide(char_to_num(argv[1][0]), char_to_num(argv[1][1]), &valid);

if (valid)

{

printf("r = %f.\n", r);

}

else 

{

printf("Divide operator error, by zero.\n");

}

// printf("char_to_num(%c) = %d.\n", '1', char_to_num('9') );

return 0;

}


2、上面例子处理除数为0的异常情况的缺陷:


(1)divide函数有三个参数,难以理解其用法


(2)divide函数调用后必须判断valid代表的结果


@1:当vaild为true时,运算结果正常

@2:当vaild为false时,运算过程出现异常

3、所以我们要将上面的divide函数的缺陷弥补起来,使其只有两个参数,用起来方便。


(1)C语言提供了两个函数:setjmp()和longjmp()进行优化。但是这两个函数不要随意的调用,因为这两个函数简单出爆,将破坏结构化程序设计的特性

在<csetjmp>头文件中


@1:

int setjmp(jmp_buf env)


:将当前上下文保存在jmp_buf结构体中


@2:

void longjmp(jmp_buf env, int val)


:从jmp_buf结构体中恢复setjmp()保存的上下文


:最终从setjmp函数调用点返回,返回值为val


(2)利用setjmp()和longjmp()函数来节省divide函数对除数为0情况时的异常处理,但这个方法也不好。


例:


#include <stdio.h>

#include <setjmp.h>


/*

* C语言中对除数为0情况下的异常处理方法,这个方法也不好,还不如用三个参数的divide解决

*

*/


static jmp_buf jmpbuff;


static double divide(double a, double b) //用valid表示异常是否发生,1为没发生、0为发生

{

const double delta = 0.000000000001;

double ret = 0;

if (!((-b < delta) && (b < delta)))

{

ret = a / b;

}

else

{

longjmp(jmpbuff, 1); //代码如果执行到这里,调用这个函数时,会跳转到setjmp这个函数调用的位置,并且这时setjmp函数的返回值为这个longjmp函数的第二个参数值

}

return ret;

}



inline static int char_to_num(const char c)

{

return (c - 48);

}



int main(int argc, char *argv[])

{


#if 0

if (2 == argc)

{

printf("argc = %d. argv[1][0] = %d.\n", argc, argv[1][0]);

}

#endif

if (setjmp(jmpbuff) == 0)

{

printf("num = %f.\n", divide((double)char_to_num(argv[1][0]), (double)char_to_num(argv[1][1])));

}

else 

{

printf("Divide operator error, by zero.\n");

}

// printf("char_to_num(%c) = %d.\n", '1', char_to_num('9') );

return 0;

}



(3)使用setjmp()和longjmp()的缺陷


@1:必然涉及到使用一个jmp_buf类型的全局变量


@2:暴力跳转导致代码可读性降低。


@3:本质还是if...else...异常处理方式,C语言中处理异常方式的经典方法。


(3)C语言中的经典异常处理方式会使得程序逻辑中混入大量的处理异常的代码, 正常逻辑代码和异常处理代码混合在一起,导致代码迅速膨胀,难以维护。



4、C++中有没有更好的异常处理方式呢?


(1)答案是有的,C++语言中已经将直接将异常的概念内置于语法当中了。可以通过关键字就可以看出来哪些代码是处理正常功能的代码,哪些代码是进行异常处理的代码。