Visual C++异常处理机制原理与应用(一)—— C/C++结构化异常处理之try-finally终止处理的使用与原理(上)

本文探讨了Visual C++中的终止型异常处理机制,旨在确保__finally块中的代码在任何情况下都能执行,以实现资源解锁和清理。通过反汇编和栈帧分析,阐述了正常执行流程和异常状态标记变量的工作原理,展示了如何在__finally块中实现解锁和清理操作。

异常处理是我们日常编程会不时用到但是却很少深入了解的部分,也是硬件、操作系统、编译器与用户程序需要密切配合才能完成的一个复杂过程。我之前学习Win32汇编时了解过Win32系统用户层的SEH(结构化异常处理),但对于Visual C++中的异常处理机制在实际编程中应该如何应用,以及它是如何利用Windows系统的SEH构建自己的异常处理机制等方面依然存在疑问。所以在这一系列文章中,我会对Visual C++中的异常处理机制的应用进行介绍、归纳和总结,并试图揭示其实现原理。

终止型异常处理

Visual C++提供的终止型异常处理机制想要实现如下功能:无论被__try保护的代码块是否能正常执行完(即不管其中是否发生异常、是否有执行流程向__try代码块外转移),__finally块中的代码都能被执行。

这在编程中是非常实用的:可以将可能发生异常或者产生错误的代码用__try块保护起来,并将解锁或者清理的代码放在__finally块中,增强程序的健壮性。下面给出两种经典应用场景:

  1. 在__finally块中执行解锁操作

    try
    {
        // 1.执行加锁、申请资源等操作,获取对某资源的使用权
        // 2.对该资源进行操作,操作过程可能发生异常
    }
    __finally
    {
        // 执行解锁、释放资源等操作,释放对该资源的使用权
    }

    这种情况下,可以避免在执行P操作获取对该资源的使用权后,在操纵该资源时发生异常或错误,使得V操作不能被执行,最终导致其他需要访问该资源的线程全部在P操作上死等,而永远无法获取到该资源的使用权。

  2. 在__finally块中执行清理操作

     try
     {
        bool bRet1 = false;
        bool bRet2 = false;
        bool bRet3 = false;
    
        // 1. 执行第1步操作
        if (/* 第1步执行不成功 */)
        {
            __leave;
        }
        bRet1 = true;   // 表明第1步执行成功
    
        // 2. 执行第2步操作
        if (/* 第2步执行不成功 */)
        {
            __leave;
        }
        bRet2 = true;   // 表明第2步执行成功
    
        // 3. 执行第3步操作
        if (/* 第3步执行不成功 */)
        {
            __leave;
        }
        bRet3 = true;   // 表明第3步执行成功
    
        // 4. 执行后续操作
    }
    __finally
    {
        if (bRet3)
        {
            // 清理、释放第3步中打开的资源
        }
        if (bRet2)
        {
            // 清理、释放第2步中打开的资源
        }
        if (bRet1)
        {
            // 清理、释放第1步中打开的资源
        }
    }

    个人认为这种情况可能是终止型异常处理最典型的应用了。Windows编程中有很多步骤都有如下特点:

    • 后一步依赖前一步的执行成功
    • 如果后一步执行失败,需要把前一步占用的资源释放掉

    如果不适用终止型异常处理来完成这些步骤,那么这部分代码将变的冗长而繁琐,在每一步操作后的判断语句中,都需要在判定执行失败后清理之前所有步骤占用的资源。

正常情况下的执行流程

首先分析执行流程正常转移的情况,此时__try块中被保护的代码不会提前退出,所以无需进行局部展开,是效率最高、开销最小的情况。

DWORD funcTest01()
{
    DWORD dwTemp = 3;
    __try
    {
        dwTemp = 4;
    }
    __finally
    {
        dwTemp = 6;
        if (AbnormalTermination())
        {
            cout << "__try块中执行时提前退出了" << endl;
        }
        else
        {
            cout << "执行流程自然转到了__finally块中" << endl;
        }
    }

    cout << dwTemp << endl;
    return 0;
}

从执行流程上分析,__try块中代码执行完后,执行流程转到__finally块中,因此对于__try块中的代码来说,属于正常终止的情况。

原理分析

反汇编代码

对应的反汇编如下:

     5: DWORD funcTest01()
     6: {
001523F0 55                   push        ebp  
001523F1 8B EC                mov         ebp,esp  
001523F3 6A FE                push        0FFFFFFFEh  
001523F5 68 E8 9E 15 00       push        159EE8h  
001523FA 68 50 29 15 00       push        offset _except_handler4 (0152950h)  
001523FF 64 A1 00 00 00 00    mov         eax,dword ptr fs:[00000000h]  
00152405 50                   push        eax  
00152406 81 C4 
### 如何在 VSCode 中终止正在运行的 C 语言程序 在 Visual Studio Code (VSCode) 中,可以通过多种方式终止正在运行的 C 程序。以下是几种常见的方法: #### 方法:通过终端快捷键终止 如果程序是在集成终端中运行,可以按下 `Ctrl+C` 组合键来中断当前进程[^2]。此操作会向运行中的程序发送个中断信号 (`SIGINT`),从而安全地中止其执行。 #### 方法二:手动关闭终端窗口 当程序运行于 VSCode 的内置终端时,可以直接右击终端区域,在弹出菜单中选择 **“关闭终端”** 来停止整个终端会话及其内部运行的所有进程[^3]。需要注意的是,这种方式不仅会结束目标程序,还会清除该终端的历史记录和其他可能存在的后台任务。 #### 方法三:利用调试模式强制停止 如果是借助调试器启动的应用,则可以在 Debug 工具栏找到红色方块样式的按钮——即代表 “Stop” 功能图标点击它即可立刻取消正在进行的任务流而无需等待自然完成周期到达终点位置之前就提前退出出来[^1]。 另外值得注意的点在于某些情况下为了防止控制台应用程序闪而过看不到输出结果我们通常会在代码最后加入句 `system("pause")`; 如果存在这样的语句那么即使按下了上述提到过的任意种办法也可能因为这个暂停命令本身还没有被执行完毕所以看起来好像没效果样实际上只要稍等片刻就会发现其实已经成功杀死了主函数之外剩余的部分逻辑处理过程了. ```c #include <stdio.h> #include <stdlib.h> float cel(float fhar); int main(){ float fhar; for(fhar=0;fhar<=300;fhar=fhar+20){ printf("%3.0f %6.1f\n",fhar,cel(fhar)); } system("pause"); // 防止运行后自动退出,需头文件 stdlib.h ,必须加 return 0; } float cel(float fhar){ float C; C=(5.0/9.0)*(fhar-32); return C; } ``` 以上就是关于如何有效率地管理以及适时适当地叫停那些由自己亲手打造出来的小小作品们啦!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值