c语言异常处理机制

  异常处理机制:setjmp()函数与longjmp()函数
  C标准库提供两个特殊的函数:setjmp() 及 longjmp(),这两个函数是结构化异常的基础,正是利用这两个函数的特性来实现异常。
  所以,异常的处理过程可以描述为这样:
  首先设置一个跳转点(setjmp() 函数可以实现这一功能),然后在其后的代码中任意地方调用 longjmp() 跳转回这个跳转点上,以此来实现当发生异常时,转到处理异常的程序上,在其后的介绍中将介绍如何实现。
  setjmp() 为跳转返回保存现场并为异常提供处理程序,longjmp() 则进行跳转(抛出异常),setjmp() 与 longjmp() 可以在函数间进行跳转,这就像一个全局的 goto 语句,可以跨函数跳转。
      举个例子,程序在 main() 函数内使用 setjmp() 设置跳转,并调用另一函数A,函数A内调用B,B抛出异常(调用longjmp() 函数),则程序直接跳回到 main() 函数内使用 setjmp() 的地方返回,并且返回一个值。

-------------------------------------------------------------------------------------------------------------------------

 jmp_buf 异常结构

使用 setjmp() 及 longjmp() 函数前,需要先认识一下 jmp_buf 异常结构。jmp_buf 将使用在 setjmp() 函数中,用于保存当前程序现场(保存当前需要用到的寄存器的值),jmp_buf 结构在 setjmp.h 文件内声明:

typedef struct

{

unsigned j_sp;  // 堆栈指针寄存器

unsigned j_ss;  // 堆栈段

unsigned j_flag;  // 标志寄存器

unsigned j_cs;  // 代码段

unsigned j_ip;  // 指令指针寄存器

unsigned j_bp; // 基址指针

unsigned j_di;  // 目的指针

unsigned j_es; // 附加段

unsigned j_si;  // 源变址

unsigned j_ds; // 数据段

} jmp_buf;

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

-------------------------------------------------------------------------------------------------------------------------

  setjmp() 与 longjmp() 函数详细说明

setjmp() 与 longjmp() 函数原型如下:

   void _Cdecl longjmp(jmp_buf jmpb, int retval);

   int _Cdecl setjmp(jmp_buf jmpb);

_Cdecl 声明函数的参数使用标准C的进栈方式(由右向左)压栈,_Cdecl 是C语言的一种调用约定,除此以外,PASCAL 也是调用约定之一。C标准调用约定(_Cdecl)所声明的函数不自动清除堆栈,这一事务由调用者自行负责——这也是C可以支持不固定个数的参数的原因。此外,这一调用约定将在函数名前添加一个下划线字符,如某一函数声明为:

int cdecl DoSomething(void);

编译时将自动为 DoSomething 加上下划线前缀,即函数名变为:    _DoSomething。

setjmp() 与 longjmp() 函数都使用了 jmp_buf 结构作为形参,它们的调用关系是这样的:

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

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

    异常处理过程

先来对比(参考)一下 C++ 的异常处理,C++ 在语言层上便添加了异常处理机制,使用 try 块来包含那些可能出现错误的代码,你可以在 try 块代码中抛出异常,C++ 使用 throw 来抛出异常。抛出异常后,将转到异常处理程序中执行,C++ 使用 catch 块来包含那些处理异常的代码,catch 块可以接收不同类型的异常。需要说明的是,throw 一般不在 try 块内的代码中抛出异常,try 块内的代码调用了别的函数,如函数A,函数A 又调用了函数 B,throw 可以在函数B中抛出异常,或者更深的函数调用层,无论如何,只要有异常抛出,程序将转到 catch 处执行。

C中如何实现,或者明确地说是模拟这一功能?

下面介绍的是一些简单的方法。

现在假设 longjmp() 第二个值为1,即 setjmp() 第二次将返回1。我们使用一组简单的宏来替代 setjmp() 和 longjmp() 以便使用:

首先定义一个全局的异常:

jmp_buf Jump_Buffer;

因为 setjmp() 第一次调用初始化后返回0,第二次返回非0,可以这样定义一个宏使得它功能接近于 C++ 的 try。

#define try if(!setjmp(Jump_Buffer))

当 setjmp() 函数第一次0 时,取非为真,则执行 try 块内的代码,如:

try {

Test();

}

当因为调用 longjmp() 抛出异常而导致 setjmp() 第二次返回时(程序将会转到 setjmp() 函数处返回,这时,这时应该执行的是异常处理代码。longjmp() 使 setjmp() 函数返回非0,if(!setjmp(JumpBuffer)) 中将值取非则为假,是以,异常处理放在其后应该使用一个 else:

#define catch else

如此看起来便跟 C++ 相似了,setjmp() 函数的第二次返回导致 if() 中表达式值为假,刚好使 catch 块得以执行,如:

try  {

Test();

} catch {

puts("Error");

}

实现如 C++ 的 throw 语句,事实上以宏替换 longjmp(jmp_buf, int) 的调用:

#define throw longjmp(Jump_Buffer, 1)

下面的例程解释如何使用这些宏:
-------------------------------------------------------------------------------------------------------------------------

#include"stdio.h"
#include"conio.h"
#include"setjmp.h"
jmp_buf Jump_Buffer;
#define try if(!setjmp(Jump_Buffer))
#define catch else
#define throw longjmp(Jump_Buffer,1)
int Test(int T)
{
    if(T>100)
        throw;
    else
          puts("OK.");
    return 0;
}
int Test_T(int T)
{
    Test(T);
    return 0;
}
int main()
{
    int T;
    try{
          puts("Input a value:");
          scanf("%d",&T);
          T++;
          Test_T(T);
      } catch{
          puts("Input Error!");
      }
    getch();
    return 0;
}

作者:tian_dao_chou_qin
来源:CSDN
原文:https://blog.csdn.net/tian_dao_chou_qin/article/details/6386621

原文地址:https://www.cnblogs.com/sshbit/p/10360936.html

时间: 2024-10-22 08:40:38

c语言异常处理机制的相关文章

Objective-C try/catch异常处理机制原理。

http://www.cnblogs.com/markhy/p/3169035.html Objective-C使用@try @catch @finally来捕获并处理异常.处理异常需要用到NSException类,它是所有异常的基类.你可以直接使用NSException类来捕获异常,也可以继承一个新的类. Objective-C是C语言的扩充,它的异常处理机制是通过C标准库提供两个特殊的函数setjmp()和longjmp()函数实现的.如果对C的异常处理机制和setjmp.longjmp函数

【Go语言】错误与异常处理机制

①error接口 Go语言中的error类型实际上是抽象了Error()方法的error接口 type error interface { Error() string } Go语言使用该接口进行标准的错误处理. 对于大多数函数,如果要返回错误,大致上都可以定义为如下模式,将error作为多种返回值中的最后一个,但这并非是强制要求: func Foo(param int)(n int, err error) { // ... } 调用时的代码建议按如下方式处理错误情况: n, err := Fo

C语言中的异常处理机制

#define try if(!setjmp(Jump_Buffer)) 返回try现场后重新执行判断,所以有两次执行. http://blog.csdn.net/tian_dao_chou_qin/article/details/6386621 1.概述 什么是异常?异常一般指的是程序运行期(Run-Time)发生的非正常情况.异常一般是不可预测的,如:内存不足.打开文件失败.范围溢出等.UNIX 使用信号给出异常,并当发生异常时转跳到信号处理过程进行异常处理.DOS下的信号对比UNIX系统而

基于返回值的错误处理与异常处理机制

基于返回的错误处理和异常处理机制的比较 一.基于返回值的错误处理机制 对于传统的面向过程语言比如说C语言,通常使用基于返回值的错误处理机制,即通过在程序中定义程序出错时的返回值,比如说0代表操作成功,而1代表操作失败. 这种错误处理机制的好处在于有时候即使出现了异常程序或许也可以继续执行(但是最终执行结果或许不对,而这就导致查错比较困难,因为有时候仅仅通过函数返回值我们并不知道程序出错的原因究竟是什么,比如说程序操作失败返回值为0,我们在控制台上看到程序输出为0,但是除此之外没有其他额外的信息帮

Java从零开始(3)异常处理机制

异常处理是程序设计中一个非常重要的方面,也是程序设计的一大难点,从C开始,你也许已经知道如何用if...else...来控制异常了,也许是自发的,然而这种控制异常痛苦,同一个异常或者错误如果多个地方出现,那么你每个地方都要做相同处理,感觉相当的麻烦! Java语言在设计的当初就考虑到这些问题,提出异常处理的框架的方案,所有的异常都可以用一个类型来表示,不同类型的异常对应不同的子类异常(这里的异常包括错误概念),定义异常处理的规范,在1.4版本以后增加了异常链机制,从而便于跟踪异常!这是Java语

[每日一题]说说异常处理机制和最佳实践

这个问题仁者见仁智者见智,每个人心中的最佳实践不见得一致,但是你要有想法,这个很关键,如果连思考都没有思考过,那就不太好了. 很多高级语言都提供了异常处理,比如Java.Python.Ruby,比较底层的语言,比如C,没有提供异常机制,最近时兴的Golang,也没有提供通常的try-catch异常机制. 异常机制是必须的么?显然不是,因为我们通常可以用多个返回值来解决,如果语言本身不支持多返回值,那异常机制就是必须的,否则这个语言写起来真的会很痛苦,你想想是不是这样?:) 异常,故名思议,就是不

JAVA学习之 异常处理机制

今天就来说说java的异常处理机制,异常处理不是第一接触,尤其是写过很多c#的代码,基本都会写到异常处理的代码,其实c#的异常处理与java的异常处理基本都是一样的,只是在一些细节上不是很一样.今天就来说说关于Java的异常处理机制. 一.为什么需要 1.原因很简单就是错误是无法避免的,为什么这么说:我们的程序都是需要与外界交互的,但是外界环境是没有办法控制的,例如要访问的文件不存在,突然网络中断,或者操作失误等等都在所难免.还有代码是程序员写出来的,有bug也在情理之中. 2.异常无法避免,但

Exception异常处理机制

为什么要自定义自己的Exception ,Java Exception机制与传统的C语言的异常处理机制有什么不同,这种Exception机制的意义在什么地方?接下来咱就来和你一起探讨Exception 的优缺点. 早期的C语言的异常处理机制,通常是我们人为的对返回结果加一些标志来进 行判定,比如发生错误返回什么标志,正常情况下我们又是返回什么标记,而这些都不是语言本身所赋予我们的,而对于C语言这种机制又有什么问题哩?为什么新 一代的语言 Java Ruby C# 等都用Exception机制而不

Java异常处理机制难点解惑-用代码说话

是否需要看这篇文章? 下面的例子中,如果正常执行返回值多少? 如果出现了ArithmeticException返回值多少? 如果出现非ArithmeticException(如NullPointerException)返回值多少? 如果你了解这个例子说明的问题,并了解例子中三种情况下的执行细节,这篇文章你就不用浪费时间看了. 例子: public int testException_finally(){ int x; try { x = 1; //int y = 1/0; //放开此处,出现Ar