Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4562599
  • 博文数量: 385
  • 博客积分: 21208
  • 博客等级: 上将
  • 技术积分: 4393
  • 用 户 组: 普通用户
  • 注册时间: 2006-09-30 13:40
文章分类

全部博文(385)

文章存档

2015年(1)

2014年(3)

2012年(16)

2011年(42)

2010年(1)

2009年(2)

2008年(34)

2007年(188)

2006年(110)

分类: C/C++

2008-09-13 17:56:59


异常(Exception)是程序可能检测到,运行时刻不正常的情况,如被0除、数组越界访问或者堆空间申请失败等等。

标准C并没有提供异常处理机制,但是提供了两个特殊的函数:setjmp()和longjmp(),这两个函数是结构化异常的基础,C里可以利用这两个函数的特性来实现异常。在头文件setjmp.h里有两个函数一个类型(jmp_buf):

int setjmp ( jmp_buf env );
void longjmp (jmp_buf env, int val);

C.1 jmp_buf是一个结构体,用于保存当前程序现场(保存当前需要用到的寄存器的值),结构如下:

  1. typedef struct
  2.           {
  3.                         unsigned j_sp;  // 堆栈指针寄存器
  4.                   unsigned j_ss// 堆栈段
  5.                  unsigned j_flag;  // 标志寄存器
  6.                   unsigned j_cs// 代码段
  7.                   unsigned j_ip// 指令指针寄存器
  8.                   unsigned j_bp;// 基址指针
  9.                   unsigned j_di// 目的指针
  10.                   unsigned j_es;// 附加段
  11.                   unsigned j_si// 源变址
  12.                   unsigned j_ds;// 数据段
  13.           } jmp_buf;

由上述结构可知,jmp_buf保存程序当前寄存器的值,以确保使用longjmp()后可以调回到该执行点上继续执行。

C.2 setjmp()和longjmp()组合其实很类似于goto语句,setjmp()首先设置一个跳转点,相当于goto里为goto语句设置一个label一样,然后在其后的代码中任意地方调用longjmp()就会跳转回到那个跳转点上,就像使用goto语句回到label处一样。不同的是,goto只能在局部域内跳转,而setjmp/longjmp可以跨函数跨文件跳转。setjmp() 与 longjmp() 函数详细说明如下:

首先调用 setjmp() 函数来初始化 jmp_buf 结构变量 jmpb,将当前CPU中的大部分影响到程序执行的寄存器的值存入 jmpb,为 longjmp() 函数提供跳转,setjmp() 函数是一个有趣的函数,它能返回两次,它应该是所有库函数中唯一一个能返回两次的函数,第一次是初始化时,返回零,第二次遇到 longjmp() 函数调用后,longjmp() 函数使 setjmp() 函数发生第二次返回,返回值由 longjmp() 的第二个参数给出(整型,这时不应该再返回零)。

在使用 setjmp() 初始化 jmpb 后,可以在其后的程序中任意地方使用 longjmp() 函数跳转回setjmp() 函数的位置,若想跳转回刚才设置的 setjmp() 处,则 longjmp() 函数的第一个参数是 setjmp() 所初始化的 jmpb 变量,这也说明一件事,即 jmpb 这个变量,一般需要定义为全局变量,否则,若是局部变量,当跨函数调用时就几乎无法使用(除非每次遇到函数调用都将 jmpb 以参数传递,然而明显地,是不值得这样做的);longjmp() 函数的第二个参数是传给 setjmp() 的第二次返回值,这在介绍 setjmp() 函数时已经介绍过。

这两个函数的使用例子如下:

static jmp_buf  buf
 
main()    
{
    
if(setjmp(buf)!=0) //第一次调用的时候返回0,所以跳过if,去执行longjmp,然后回到这里返回值变成了1 
    
{
        
printf("%d "b);  
        
exit(0);
    
}
    
longjmp(buf , 1);
}

Cpp.1 C++提供了一些内置的语言特性来抛出异常(throw)并处理异常这些语言特性将激活了一种运行时刻机制通过这种机制可在C++程序的两个无关常常是独立开发的部分进行异常通信。C++的异常处理机制被称为是不可恢复的nonresumptive :一旦异常被处理,程序的执行就不能够在异常被抛出的地方继续。

抛出异常可通过throw 表达式 来实现,而这里表达式不能只是一个类型,而必须是内置的类型变量或者自定义的异常类对象(临时对象也行)。

异常处理通过如下形式:

try
{
    
//执行的代码,代码内部一定要包含 throw 表达式
   .......
}
catch (exception e1)
{
    
//异常处理代码
    .......
}
catch (exception e2)
{
    
//异常处理代码
    .......
}
....

C++异常处理代码是catch 子句(catch clause)。 当一个异常被try 块中的语句抛出时,系统通过查看跟在try 块后面的catch 子句列表,来查找能够处理该异常的catch 子句。一个catch 子句由三部分构成:关键字catch 、在括号中的单个类型或单个对象声明(被称作异常声明exception declaration) 以及复合语句中的一组语句。catch字句列表是根据throw的对象类型来选择的,两处的对象类型必须一致,或者可以隐式的转换,如:继承类到基类,而且throw会把抛出的对象类型复制构造(如果是内置类型,就直接赋值)给catch子句中的当个对象申明。

可以总结如下:throw就是抛出一个异常对象,而catch就是捕捉这个对象,在运行时确定使用那个catch子句。如果在catch子句中要用到那个异常对象,那么catch后面的括号中就填写异常对象的申明,这样可以将throw的异常对象赋值给申明的那个对象;如果catch子句用不到那个异常对象,那么catch后面的括号里直接填写异常对象类型就可以了,只是单纯为了匹配使用。

阅读(2195) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~