本章我主要看了异常的接口,具体的实现没有怎么看。个人觉得会用异常就可以了。断言的概念等也有了一些了解。
1、程序中的三种错误:用户错误、运行时错误和异常。
①用户错误是预期会发生的,因为错误的用户输入就可能会导致用户错误。例子如命名不存在的文件、在电子表格中指定格式错误的数字以及向编译器提交语法错误的源程序等。必须处理用户错误的函数会返回错误码,这种错误时计算过程的一个普通组成部分。
②已检查的运行时错误不是用户错误。它们从来都是非预期的,总是表明程序出现了bug。因而使用断言(assertion)来捕获这种错误。断言总是导致程序结束,具体的结束方式或许取决于机器或应用程序。
③异常(exception)介乎用户错误和程序bug之间。它是可能比较罕见的错误,或许是非预期的,但从异常恢复也许是可能的。异常情况如算术运算上溢和下溢以及栈溢出;又如由用户发起的,按下一个“中断”键或写文件时遇到写入错误;还如当有限的资源用尽时也可能发生异常,如应用程序内存不足时,或用户指定了过大的电子表格文件时。
2、异常
异常由应用程序引发,由恢复代码处理(如果能恢复的话)。异常的作用域是动态的:当一个异常被引发时,它由最近实例化的处理程序处理。将控制权转移到处理程序。在C语言中,标准库函数setjmp和longjmp是建立结构化的异常处理设施的基础。简言之,setjmp实例化一个处理程序,而longjmp引发一个异常。
3、Except接口
Except接口将setjmp/longjmp设施封装在一组宏和函数中,这些宏和函数相互协作,提供了一个结构化的异常处理设施。它并不完善,但避免了一些错误。而其中的宏很清楚地标识出了使用异常的位置。
异常是Except_T类型的全局或静态变量:
#define T Except_T
typedef struct T
{
const char *reason; /*Except_T结构只有一个字段,可以初始化为一个描述异常信息的字符串。在发生未处理的异常时,将输出该字符串。*/
}T;
异常处理程序需要操作异常的地址。因而异常必须是全局或者静态变量,使得其地址可以唯一地标识某个异常。将异常声明为局部变量或作为参数是未检查的运行时错误。
4、TRY-EXCEPT和TRY-FINALLY语句
处理程序通过TRY-EXCEPT和TRY-FINALLY语句实例化,这些语句用宏实现。这些语句处理嵌套异常并管理异常状态数据。
TRY-EXCEPT语句的语法如下:
TRY
S
EXCEPT(e1)
S1
EXCEPT(e2)
S2
...
EXCEPT(en)
Sn
ELSE
S0
END_TRY
TRY-FINALLY语句的语法如下: /*注意,无论S是否引发了异常,都会执行S1*/
TRY
S
FINALLY
S1
END_TRY /*如果S没有引发异常,将执行S1并继续执行END_TRY之后的语句。如果S引发了异常,将中断S的执行,控制立即转移到S1。在S1执行之后,导致S1执行的异常将被再次引发(re-raised),使之可以被此前实例化的处理程序处理。*/
5、断言
将断言打包起来,使之在失败时引发异常,这种做法有助于解决在产品程序中处理断言面临的两难处境。关于删除断言的原因,最常提到的两个原因是效率和含义模糊的诊断信息。
断言确实要花费时间,因此删除断言只会使程序更快。可以测量有无断言情况下执行时间的差别,但差别通常很小。
在测量表明断言开销太高时,有时可以移动断言的位置,在不失去断言好处的情况下降低其开销。
断言的更严重的问题在于,它们会导致输出诊断信息,如上文的断言失败诊断,这将迷惑用户。但删除断言,无疑是用更严重的问题代替了诊断信息。在断言失败时,程序就是错误的。如果程序继续执行,其结果是不可预测的,很可能崩溃。在断言失败之后继续执行(而不停止)的程序可能会破坏用户的数据,如,编辑器如果在断言失败后继续执行,就可能破坏用户的文件。这种行为时不可原谅的。
断言接口的实现:
static char rcsid[] = "$Id: H:/drh/idioms/book/RCS/except.doc,v 1.10 1997/02/21 19:43:55 drh Exp $";
#include "assert.h"
const Except_T Assert_Failed = { "Assertion failed" };
void (assert)(int e) {
assert(e);
}
6、断言失败时,诊断信息含义模糊的问题可以这样解决:在程序的产品版本顶层代码中放一个TRY-EXCEPT语句,捕获所有的未捕获异常,并输出更有帮助的诊断信息。
如:
#include<stdlib.h>
#include<stdio.h>
#include"except.h"
int main(int argc,char *argv[])
{
TRY
edit(argc,argv);
ELSE
fprintf(stderr,
"An internal error has occurred from which there is "
"no recovery. \nPlease report this error to "
"Technical Support at 800-777-1234.\nNote the "
"following message, which will help our support "
"staff\nfind the cause of this error.\n\n ")
RERAISE;
END_TRY;
return EXIT_SUCCESS;
}
在出现未捕获的异常时,将由该处理程序接手,指导用户报告bug,然后再输出含义模糊的异常诊断信息。