软件开发的家园,编程爱好者的天地.

现在是:北京时间 2016/4/14 上午11:50:51 星期四

设为首页  |  加入收藏  |  网站地图

当前位置: 第八基地首页 > C/C++/VC > VC/VC++ >
C 编译器怎么实现异常处理3
发布于:第八基地 来源:互联网 作者:天堂路上 时间:2011-11-04 点击:123

C和异常

再回头来说我们在第一节里说到的EXCEPTION_REGISTRATION结构,这个结构是用来注册操作系统的异常回调函数的,当异常发生时,该函数将被调用。VC扩展了异常回调函数得语法,增加了两个新的参数:

structEXCEPTION_REGISTRATION{EXCEPTION_REGISTRATION*prev;DWORDhandler;intid;DWORDebp;};

VC里,除了一些例外,在每个函数的头部生成EXCEPTION_REGISTRATION结构的局部变量(作者注:编译器可能在一个函数里根本不生成任何异常相关的代码,如果函数里没有try或者所有的局部对象都没有可供调用的析构函数(译注:当异常产生时,要把堆栈顶到catch到异常那点之间的局部变量都释放掉,这是异常处理一个重要责任。如果这些局部变量都不需要去特别释放,比如int变量或简单结构变量,只要把堆栈指针指过来就可以了,异常处理在这一段里就是没有必要的,因此编译器识别了这样的情况,作个一定的优化))。上面结构的ebp和前面说的堆栈帧里的ebp指针是重叠在一起的(译注:请参看C编译器怎么实现异常处理2),函数在开始时把这个结构创建在它的堆栈上,同时把这个结构注册到操作系统(译注:就是把FS:[0]的位置改成这个结构的指针);在函数的结尾恢复调用者在系统注册的EXCEPTION_REGISTRATION结构(译注:就是把FS:[0]改成结构里的prev),我将在下一节讨论EXCEPTION_REGISTRATION结构里的id域的重要性。当VC编译一个函数,它生成函数的两套数据:a)异常的回调函数b)一个包含重要信息的数据结构,这些重要信息比如,各个catch块,它们的地址,关心的异常的种类等等,我将在下一节更多的谈论它图4显示了如果考虑异常处理,程序运行时堆栈的情况。Widget的异常回调是在异常链的头部(异常链的头部就是FS:[0]指向的内容),FS:[0]的内容就是在Widget头部定义的EXCEPTION_REGISTRATION结构的指针。当异常产生时,异常处理把Widget的函数信息结构的地址(就是EXCEPTION_REGISTRATION的指针)传递给__CxxFrameHandler函数,__CxxFrameHandler函数检查这个数据结构,察看函数里是否有catch块对当前的异常感兴趣,如果没有找到,函数返回ExceptionContinueSearch给操作系统,操作系统获得异常处理链里的下一个节点,再调用这个节点的异常处理函数(这个节点应该是在本函数的调用者定义的)

这个动作一直持续到异常处理找到一个对这个异常感兴趣的catch块,找到了匹配的catch块以后,程序不再返回到操作系统。但是在它调用catch块前(在函数信息结构里有这个异常回调函数的指针,看图4),异常处理必须执行堆栈释放:清除这个函数之前的所有函数的堆栈帧。清除堆栈帧的动作有点复杂,异常处理必须找到所有在异常产生时还没有结束的函数,找到每个函数所有的局部变量,调用每个变量的析构函数。我将在以后的章节里更详细的讨论这点。异常处理是这样一个任务,当异常处理时清除异常处理所在的函数帧。这个操作是从FS:[0]指向的位置,也就是异常处理链头开始的,然后沿着链一个节点一个节点的通知它所在的堆栈将要被回收,收到通知的节点,调用所在帧的所有局部对象的析构然后返回,这个过程一直延续到抛出的异常被捕获到。因为catch块也是某个函数的一部分,所以它也用了所在函数的函数帧来保存数据。因此异常处理的一个catch块进入的时候会激活所在函数的那一帧(译注:就是会把ebp改到那个函数去,而esp是不动的,简单的说,在catch函数块里执行的时候,ebp是指向所在函数的帧顶,而esp可能在非常远的堆栈顶端)。同时,每个能能捕获异常的catch块(就是异常种类和catch块要捕获的异常种类一致)实际上只有一个参数,就是这个异常对象的拷贝或者是异常对象的引用拷贝。catch块知道怎么从函数信息结构中拷贝这个异常对象,编译器已经产生了足够多的信息(译注:就是根据传入的id,来判断怎么复制异常)。
下一篇:接触VC
对我有帮助
(0)
0%
对我没帮助
(0)
0%
返回顶部
在线反馈
在线反馈