try{} catch(…){}

以前都是用try{} catch(…){}来捕获C++中一些意想不到的异常, 今天看了Winhack的帖子才知道,这种方法在VC中其实是靠不住的。例如下面的代码:


try
{
BYTE* pch ;
pch = ( BYTE* )00001234 ; //给予一个非法地址

*pch = 6 ; //对非法地址赋值,会造成Access Violation 异常
}
catch(…)
{
AfxMessageBox( “catched” ) ;
}


这段代码在debug下没有问题,异常会被捕获,会弹出”catched”的消息框。 但在Release方式下如果选择了编译器代码优化选项,则VC编译器会去搜索try块中的代码, 如果没有找到throw代码, 他就会认为try catch结构是多余的, 给优化掉。 这样造成在Release模式下,上述代码中的异常不能被捕获,从而迫使程序弹出错误提示框退出。

那么能否在release代码优化状态下捕获这个异常呢, 答案是有的。 就是__try, __except结构, 上述代码如果改成如下代码异常即可捕获。


__try
{
BYTE* pch ;
pch = ( BYTE* )00001234 ; //给予一个非法地址

*pch = 6 ; //对非法地址赋值,会造成Access Violation 异常
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
AfxMessageBox( “catched” ) ;
}

但是用__try, __except块还有问题, 就是这个不是C++标准, 而是Windows平台特有的扩展。 而且如果在使用过程中涉及局部对象析构函数的调用,则会出现C2712 的编译错误。 那么还有没有别的办法呢?

当然有, 就是仍然使用C++标准的try{}catch(..){}, 但在编译命令行中加入 /EHa 的参数。这样VC编译器不会把try catch模块给优化掉了。

找到一篇比较好的英文文章谈这个问题: http://members.cox.net/doug_web/eh.htm

用C++10 年多了 , 居然这么基础的问题都搞错, 真是汗颜。 要加紧学习啊, Stay Hungry, Stay Foolish!

《try{} catch(…){}》上有5条评论

  1. 以前曾有过这个的疑问,赶紧在Linux上试了一下,Segment fault之后也不能被catch住。到网上查了一下,要想catch住这种系统信号,可以在信号处理函数中加上throw语句,并且在用g++编译时,加上non-call-exceptions

    Since processor exceptions may occur anywhere in the code, the stack un-
    winding libraries must be prepared to handle exceptions within any context.
    This functionality is enabled by a special GNU g++ compiler °ag, \-fnon-call-
    exceptions". Without the use of this °ag, only explicitly thrown exceptions are
    handled. This option is typically used from user-space code that attempts to
    throw exceptions from signal handlers. In the kernel, this allows an exception to
    be correctly dispatched even in the absence of an explicit throw call.

  2. 非法的内存访问属于Win32异常范畴,C++语言的try catch根本就不会去处理这种异常,也没有这种能力。

    Release也不会那样去优化,可以做如下代码验证:
    try
    {
    *(int *)NULL = 11;
    throw 111;
    }
    catch (…)
    {
    cerr<<"excetion"<<endl;
    }
    这里是有throw的,Release不会优化掉了,但是仍旧崩溃。

    这段代码在Debug下也崩溃,博主能捕获到异常可能是因为那个地址真的就可写。最好直接用地址0来做测试。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注