用C语言处理程序的异常简介
平时我们用c语言来写程序,经常会遇见一种情况就是使用了一个NULL的指针,然后结果显而易见是程序挂掉了,这个就是所谓的异常。这个显然是一个废话,但是本文研究的主题就是要怎样才能捕获这种挂掉的异常,然后让程序正确运行。这个就是需要一点技术了,当然对于C++这个异常的捕获try语句是必修课,但是对于C语言来说有点问题了,虽然有__try这个类似的异常处理,但是毕竟不是C标准里面的东西,这里介绍一种标准C语言通过调用API简单有效的捕获程序异常的方法首先有这样的一段程序
程序1
#include <stdio.h>
int i=0;
int *p=NULL;
int main()
{
*p=0;
return 0;
}
编译执行,谁都知道结果是程序挂掉。但是不知道大家有没有研究过这个挂掉的过程,有兴趣的话自己去研究一下吧,最重要的一点记住就行。Windows 程序设计中最重要的理念就是消息传递,事件驱动。我们可以把异常也当作是一种消息,应用程序发生异常时就触发了该消息并告知系统。系统接收后同样会找它的“回调函数”,也就是我们的异常处理例程。
如果你这些看不懂,不要紧,下面来看看实例吧:)
首先要定义一个异常处理函数,它的函数原型是:
long WINAPI ExceptionFilter(EXCEPTION_POINTERS * lParam)
这个不是你程序当中要调用的,是windows系统调用的,所以一定要确保函数是这种原型。至于EXCEPTION_POINTERS这个结构储存关于异常的类型地点等一些信息,这个很复杂,估计把它介绍完我今天晚上不要睡觉了(现在是晚上0:34)。这个函数有一个返回值long类型的,但是有效的返回值只有三个,并且这个返回值是送给windows的,让windows根据它的值来决定这个程序是继续运行还是关闭。
三个有效的值分别是
EXCEPTION_EXECUTE_HANDLER=1 已处理这个异常,程序正常结束
EXCEPTION_CONTINUE_SEARCH = 0 不处理这个异常转交windows处理,就是弹出一个程序错误的框框
EXCEPTION_CONTINUE_EXECUTION = -1 已修复这个错误,返回错误出现的地方继续执行,8过这个是相当困难的:P
程序2 捕获一个异常然后让程序正常结束
#include <stdio.h>
#include <windows.h>
int i=0;
int *p=NULL;
long WINAPI ExceptionFilter(EXCEPTION_POINTERS * lParam)
{
puts("DADA...");
return 1;
}
int main()
{
SetUnhandledExceptionFilter(ExceptionFilter);
*p=0;
return 0;
}
SetUnhandledExceptionFilter(ExceptionFilter);这条语句设定了一个异常处理函数,就是告诉windows如果如果本程序出了一个错误就去执行ExceptionFilter函数。
*p=0;爆出了一个无效内存访问的错误,接着windows就会去调用以前注册过的ExceptionFilter函数。这个函数的功能就是输出DADA…这个字符串,然后返回1给windows来结束这个程序。
整个过程就是这样的,注意运行的时候选择Start Without Debug否则只会让调试器爆出一个错误的提示。
接下去研究怎么捕获异常来修正这个错误,让程序继续运行。就是遇到异常就先给p赋值&i再返回出错的地方继续执行。这里就要用到一个setjmp的东西了,这个是c标准里面的,我简单介绍一下
longjmp
语法:
#include <setjmp.h> void longjmp( jmp_buf envbuf, int status );
功能: 函数使程序从前次对setjmp()的调用处继续执行。参数envbuf一般通过调用setjmp()设定。参数status 为setjmp()的返回值,用来指示不同地点longjmp()的执行. status 不能设定为零。
setjmp
语法:
#include <setjmp.h> int setjmp( jmp_buf envbuf );
功能: 函数将系统栈保存于envbuf中,以供以后调用longjmp()。当第一次调用setjmp(),它的返回值为零。之后调用longjmp(),longjmp()的第二个参数即为setjmp()的返回值。
程序3:调用longjmp来纠正错误,让程序继续运行
#include <stdio.h>
#include <windows.h>
#include <setjmp.h>
int i=0;
int *p=NULL;
int status;
jmp_buf envbuf;
long WINAPI ExceptionFilter(EXCEPTION_POINTERS * lParam)
{
p=&i;
//修改p至正确值
longjmp(envbuf,status);
//跳回到setjmp设定的地方
return 1;
//这句是摆设没有意义
}
int main()
{
SetUnhandledExceptionFilter(ExceptionFilter);
//设定异常处理函数
status=setjmp(envbuf);
//设定longjmp跳跃目的地
*p=0;
puts("DADA...");
//屏幕上出现DADA...表示程序正确运行了:)
return 0;
}
就是这么简单.西西:)
写在后面的话
有一个问题,用EXCEPTION_CONTINUE_EXECUTION来代替longjmp返回出错的地方不是更简单吗,答案是不可以。究竟是为什么,有兴趣的人可以自己去把程序反汇编研究一下。出错的地方指的是发生异常的指令所在的位置而不是这条语句的位置。
我的邮箱lingluoz@
P.S.
睡了。。。好困。。