C++编译器如何实现异常处理
v FS:[0], EAX } //产生一个异常 int j = 10 / g_div; //异常,除零溢出 } int main() { bar(); return 0; } /*-------输出------------------- In the exception handler Just a demo. exiting... ---------------------------------*/ 注意EXCEPTION_REGISTRATION必须定义在栈上,并且必须位于比上一个结点更低的内存地址上,Windows对此有严格要求,达不到的话,它就会立刻终止进程。 函数和堆栈 堆栈是用来保存局部对象的连续内存区。更明确的说,每个函数都有一个相关的栈桢(stack frame)来保存它所有的局部对象和表达式计算过程中用到的临时对象,至少理论上是这样的。但现实中,编译器经常会把一些对象放到寄存器中以便能以更快的速度访问。堆栈是一个处理器(CPU)层次的概念,为了操纵它,处理器提供了一些专用的寄存器和指令。 图1是一个典型的堆栈,它示出了函数foo调用bar,bar又调用widget时的情景。请注意堆栈是向下增长的,这意味着新压入的项的地址低于原有项的地址。 通常编译器使用EBP寄存器来指示当前活动的栈桢。本例中,CPU正在运行widget,所以图中的EBP指向了widget的栈桢。编译器在编译时将所有局部对象解析成相对于栈桢指针(EBP)的固定偏移,函数则通过栈桢指针来间接访问局部对象。举个例子,典型的,widget访问它的局部变量时就是通过访问栈桢指针以下的、有着确定位置的几个字节来实现的,比如说EBP-24。 上图中也画出了ESP寄存器,它叫栈指针,指向栈的最后一项。在本例中,ESP指着widget的栈桢的末尾,这也是下一个栈桢(如果它被创建的话)的开始位置。 处理器支持两种类型的栈操作:压栈(push)和弹栈(pop)。比如, 的作用是从ESP所指的位置读出4字节放到EAX寄存器中,并把ESP加上(记住,栈是向下增长的)4(在32位处理器上);类似的, push EBP 编译器编译一个函数时,会在它的开头添加一些代码来为其创建并初始化栈桢,这些代码被称为序言(prologue);同样,它也会在函数的结尾处放上代码来清除栈桢,这些代码叫做尾声(epilogue)。 一般情况下,序言是这样的: Push EBP ; 把原来的栈桢指针保存到栈上 尾声所做的正好和序言相反,它必须把当前栈桢从栈上清除掉: Mov ESP, EBP 一旦CPU遇到返回指令,它就要做以下两件事:把返回地址从栈中弹出,然后跳转到那个地址去。返回地址是主调函数执行call指令调用被调函数时自动压栈的。Call指令执行时,会先把紧随在它后面的那条指令的地址(被调函数的返回地址)压入栈中,然后跳转到被调函数的开始位置。图2更详细的描绘了运行时的堆栈。如图所示,主调函数把被调函数的参数也压进了堆栈,所以参数也是栈桢的一部分。函数返回后,主调函数需要移除这些参数,它通过把所有参数的总体积加到ESP上来达到目的,而这个体积可以在编译时知道: Add ESP, args_size |
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |