Windows下return,exit和ExitProcess的区别和分析- -

【Collibra 驱动 IPD 数据血缘治理】:三层图谱分层展示体系与全链路追踪的技术业务实践 (应用级/数据级/字段级)是理解复杂数据流动的核心手段。其价值不仅体现在可视化的直观性,更在于通过。技术保障血缘不可篡改性,进一步提升IPD数据治理的可靠性。,解决了跨层级血缘分析的性能与可用性矛盾。数据血缘图谱要素定义(字节跳动/腾讯云/信安标委)实现对IPD数据的精准追踪,而数据血缘图谱的。分层交互设计标准(智慧城市数据中台规范)Collibra技术文档(2024)字段级血缘的挑战与解决路径。自动优化血缘路径,并深化。字段级血缘技术实现方案。Collibra通过。图数据库性能优化原理。 阅读详情

通常,我们为了使自己的程序结束,会在主函数中使用return或调用exit()。在windows下还有ExitProcess()和TerminateProcess()等函数。
本文的目的是比较以上几种结束程序的方式的区别,并分析其原理。


首先我们用一个例子来说明几种结束方式的区别。
测试环境为Windows XP HOME SP2,编译器为Visual Studio.net 2003

测试代码如下:
#include
#include
#include

class Test
{
public:
 Test (int i) {m_i=i; printf ('construct %dn', m_i);};
 ~Test () {printf ('destruct %dn', m_i);};
private:
 int m_i;
};

Test t_1 (1);

int main(int argc, char* argv[])
{
 Test t_2 (2);
 printf('Hello World!n');
// return 0;
// exit (0);
// ExitProcess (0);
}

我们的目标是察看两种结束方式有什么不同。

程序在运行的结果为:

使用return 0结束时:
construct 1
construct 2
Hello World!
destruct 2
destruct 1

使用exit (0)结束时:
construct 1
construct 2
Hello World!
destruct 1

使用ExitProcess (0)结束时:
construct 1
construct 2
Hello World!

从结果上我们可以看出来,采用return来结束进程可以正确的析构全局和局部对象。而采用exit()来结束进程时全局对象可以正确析构,但局部对象没有正确析构。采用ExitProcess(0)结束时全局和局部对象都没有正确析构。

为什么会出现这样的情况呢?
《Windows核心编程》中我们可以得到以下解释:
'当主线程的进入点函数(WinMain、wWinMain、main或wmain)返回时,它将返回给C/C++运行期启动代码,它能够正确地清楚该进程使用的所有C运行期资源。当C运行期资源被释放之后,C运行期启动代码就显式的调用ExitProcess,并将进入点函数返回的值传递给它。'

那么,通过跟踪代码我们可以发现:
return 0实际上执行了以下操作:
 return 0;
00401035  mov         dword ptr [ebp-0D4h],0
0040103F  lea         ecx,[t_2]
00401042  call        Test::~Test (4010F0h)
00401047  mov         eax,dword ptr [ebp-0D4h]
}
0040104D  push        edx 
0040104E  mov         ecx,ebp
00401050  push        eax 
00401051  lea         edx,ds:[401072h]
00401057  call        _RTC_CheckStackVars (4011E0h)
0040105C  pop         eax 
0040105D  pop         edx 
0040105E  pop         edi 
0040105F  pop         esi 
00401060  pop         ebx 
00401061  add         esp,0D8h
00401067  cmp         ebp,esp
00401069  call        _RTC_CheckEsp (4011B0h)
0040106E  mov         esp,ebp
00401070  pop         ebp 
00401071  ret             
在ret之后,程序返回到启动main函数的代码,并执行以下操作:
            if ( !managedapp )
                exit(mainret);
            _cexit();

可见return 0上调用了局部对象t_2的析构函数。

而
void __cdecl exit (
        int code
        )
{
        doexit(code, 0, 0); /* full term, kill process */
}

void __cdecl _cexit (
        void
        )
{
        doexit(0, 0, 1);    /* full term, return to caller */
}
         
实际上程序调用了doexit函数。
static void __cdecl doexit (
        int code,
        int quick,
        int retcaller
        )
{
#ifdef _DEBUG
        static int fExit = 0;
#endif  /* _DEBUG */

#ifdef _MT
        _lockexit();        /* assure only 1 thread in exit path */
        __TRY
#endif  /* _MT */

            if (_C_Exit_Done == TRUE)                               /* if doexit() is being called recursively */
                    TerminateProcess(GetCurrentProcess(),code);     /* terminate with extreme
prejudice */
            _C_Termination_Done = TRUE;

            /* save callable exit flag (for use by terminators) */
            _exitflag = (char) retcaller;  /* 0 = term, !0 = callable exit */

            if (!quick) {

                /*
                 * do _onexit/atexit() terminators
                 * (if there are any)
                 *
                 * These terminators MUST be executed in reverse order (LIFO)!
                 *
                 * NOTE:
                 *  This code assumes that __onexitbegin points
                 *  to the first valid onexit() entry and that
                 *  __onexitend points past the last valid entry.
                 *  If __onexitbegin == __onexitend, the table
                 *  is empty and there are no routines to call.
                 */

                if (__onexitbegin) {
                    while ( --__onexitend >= __onexitbegin )
                    /*
                     * if current table entry is non-NULL,
                     * call thru it.
                     */
                    if ( *__onexitend != NULL )
                        (**__onexitend)();
                }

                /*
                 * do pre-terminators
                 */
                _initterm(__xp_a, __xp_z);
            }

            /*
             * do terminators
             */
            _initterm(__xt_a, __xt_z);


#ifndef CRTDLL
#ifdef _DEBUG
            /* Dump all memory leaks */
            if (!fExit && _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) & _CRTDBG_LEAK_CHECK_DF)
            {
                fExit = 1;
                _CrtDumpMemoryLeaks();
            }
#endif  /* _DEBUG */
#endif  /* CRTDLL */

            /* return to OS or to caller */

#ifdef _MT
        __FINALLY
            if (retcaller)
                _unlockexit();      /* unlock the exit code path */
        __END_TRY_FINALLY
#endif  /* _MT */
        if (retcaller)
            return;


        _C_Exit_Done = TRUE;

        __crtExitProcess(code);
}

其中部分源代码如下:
                if (__onexitbegin) {
00406056  cmp         dword ptr [___onexitbegin (412DA8h)],0
0040605D  je          doexit+70h (406090h)
                    while ( --__onexitend >= __onexitbegin )
0040605F  mov         edx,dword ptr [___onexitend (412DA4h)]
00406065  sub         edx,4
00406068  mov         dword ptr [___onexitend (412DA4h)],edx
0040606E  mov         eax,dword ptr [___onexitend (412DA4h)]
00406073  cmp         eax,dword ptr [___onexitbegin (412DA8h)]
00406079  jb          doexit+70h (406090h)
                    /*
                     * if current table entry is non-NULL,
                     * call thru it.
                     */
                    if ( *__onexitend != NULL )
0040607B  mov         ecx,dword ptr [___onexitend (412DA4h)]
00406081  cmp         dword ptr [ecx],0
00406084  je          doexit+6Eh (40608Eh)
                        (**__onexitend)();
00406086  mov         edx,dword ptr [___onexitend (412DA4h)]
0040608C  call        dword ptr [edx]
                }
0040608E  jmp         doexit+3Fh (40605Fh)

程序在0040608C处跳转到如下代码:
0040EC10  push        ebp 
0040EC11  mov         ebp,esp
0040EC13  sub         esp,0C0h
0040EC19  push        ebx 
0040EC1A  push        esi 
0040EC1B  push        edi 
0040EC1C  lea         edi,[ebp-0C0h]
0040EC22  mov         ecx,30h
0040EC27  mov         eax,0CCCCCCCCh
0040EC2C  rep stos    dword ptr [edi]
0040EC2E  mov         ecx,offset t_1 (412760h)
0040EC33  call        Test::~Test (4010F0h)
0040EC38  pop         edi 
0040EC39  pop         esi 
0040EC3A  pop         ebx 
0040EC3B  add         esp,0C0h
0040EC41  cmp         ebp,esp
0040EC43  call        _RTC_CheckEsp (4011B0h)
0040EC48  mov         esp,ebp
0040EC4A  pop         ebp 
0040EC4B  ret             
在这里,全局变量t_1被析构。
在doexit的最后,程序调用
        __crtExitProcess(code);

void __cdecl __crtExitProcess (
        int status
        )
{
        HMODULE hmod;
        PFN_EXIT_PROCESS pfn;

        hmod = GetModuleHandle('mscoree.dll');
        if (hmod != NULL) {
            pfn = (PFN_EXIT_PROCESS)GetProcAddress(hmod, 'CorExitProcess');
            if (pfn != NULL) {
                pfn(status);
            }
        }

        /*
         * Either mscoree.dll isn't loaded,
         * or CorExitProcess isn't exported from mscoree.dll,
         * or CorExitProcess returned (should never happen).
         * Just call ExitProcess.
         */

        ExitProcess(status);
}
在这里,终于调用到了ExitProcess。至此,全局对象t_1和局部对象t_2都完成了析构操作。

从分析过程,我们可以得出以下结论。
在Windows下,return 0 的实际执行过程是:

  • 先析构main函数内的局部对象。
  • 返回至调用main的函数。
  • 调用exit函数,由exit函数调用doexit函数,在doexit函数中完成对全局对象的析构。
  • 最后调用ExitProcess结束进程。

所以,ExitProcess不负责任何对象的析构,exit只负责析构全局对象,return 0可以析构局部对象并调用exit,因此能析构全部对象。


ZL=http://chriszz.bokee.com/896602.html  作者:chriszz

OpenClaw赚钱实录:从“养龙虾“到可持续变现的实践指南 OpenClaw已经开源3个月有余,作为深耕AI Agent领域的博主,我亲身探索了各种基于OpenClaw的赚钱方法,也踩过坑、付出了时间与精力。现在,我将这些真金白银砸出来的实战经验,毫无保留地总结成这个专栏,分享给不想从零开始摸索的你。博主在优快云已有7万+粉丝和1100万+阅读量,但这却是我的第一个付费专栏。正因如此,我比任何人都更珍惜这个“第一次”。我会像一个手艺人打磨作品一样,死磕每一篇文章的质量,并持续根据订阅者的反馈优化内容。也许你的疑问就是当篇文章的优化点。 阅读详情

相关推荐

Nest的限流方案

多维度限流(如 IP、用户 ID、接口级别、请求类型)可配置化(支持不同环境和接口动态配置)可观测性(接入日志、监控系统)高性能和分布式支持(Redis 限流器)与鉴权鉴权链路集成(结合 JWT、API Token)以下是一个“标准// 优先按用户ID限流,否则按IP在main.ts维度建议限流存储推荐 Redis 支持多实例限流 Key 设计支持 IP + 用户ID + 路由等多种维度限流策略精细化配置,接口粒度调整异常处理自定义响应体、日志告警观测性。

Bruce__taotao的博客 1196

【转】ExitProcess与TerminateProcess

进程只是提供了一段地址空间和内核对象,其运行时通过其他地址空间内的主线程来体现的。当主线程的进入点函数返回时,进程也就随之而技术。这种进程的种植方式是进程的正常退出。进程中的所有县城资源都能够得到正确的清除。除了这种进程的正常退出方式之外,优势还需要在程序中通过代码来强制结束本进程或其他进程的运行。 ExitProcess void ExitProcess(UINT uExitCode);

winson_jason的专栏 3512

从solidworks导出URDF模型

一、下载sw_urdf_exporter插件 ROS目前提供了SolidWorks转URDF的插件,叫做sw_urdf_exporter,从http://wiki.ros.org/sw_urdf_exporter下载sw2urdfSetup.exe文件,下载后点击安装,安装完成后就能在solidworks中导出URDF模型了。 二、创建solidworks模型 下面以较为简单的四轮小车为例进行说...

weixin_45168199的博客 4万+

结束程序的代码return 0 exit(0) ExitProcess(0)的不同之处

退出程序的几种方式 1、首先说下程序是如何启动的,前面的读书笔记已经详细写了,这儿再重复下 程序执行的步骤: A、 检索指向新进程的完整命令行的指针; B、 检索指向新进程的环境变量的指针; C、 对C/C++运行期的全局变量进程初始化; D、 对C运行期间内存单元分配函数和其他底层输入/输出例程使用的内存栈进程初始化; E、  为所有全局和静态C++类对

xiaolongrenstep的博客 2205

3.PCIe协议分析1-预备知识点

PCIE协议分析1主要讲一下四方面: PCI-E配置空间 PCI-E地址映射 PCI-E Bar空间 PCI-E层级结构详谈 (1)PCIE配置空间 我们过来学习PCIE的同学应该都接触各种类型的功能芯片吧,这些功能芯片连接在电路里面我们可以认为它是一个外部设备,这些设备里面有自己独有的寄存器区域,比如SDRAM里面的ID寄存器或者其他配置寄存器。好的,其实我们PCIE板卡也相当...

一个早起的程序员 4143

YOLOv8改进 | Conv篇:基于FasterBlock与C2f结构的二次创新,参数量下降70万【附保姆级代码】

通过引入FasterBlock二次创新C2f结构,我们成功地对YOLOv8模型进行了优化,显著降低了其参数量,并提升了推理速度,而精度保持不变甚至有所提升。该模型在多个实际应用场景中展现出了优异的性能,尤其是在嵌入式设备、移动端和自动驾驶等领域,具有广泛的应用潜力。未来的研究将进一步优化模型结构,探索更多的应用场景,为目标检测技术的发展做出贡献。

一键难忘的博客 3214

Windows核心编程<读书笔记四>return exit(0) ExitProcess的不同之处

【文起】爱生活,爱编码,爱蟹儿。为了我跟蟹儿的幸福,奋斗           退出程序的几种方式 1、首先说下程序是如何启动的,前面的读书笔记已经详细写了,这儿再重复下 程序执行的步骤: A、 检索指向新进程的完整命令行的指针; B、 检索指向新进程的环境变量的指针; C、 对C/C++运行期的全局变量进程初始化; D、 对C运行期间内存单元分配函数和其他底层输入

豆浆爱蟹蟹 1733

AI时代的软件质量保证新方法论

3.2.1 质量保证的方法论 质量保证(Quality Assurance, QA)是一种系统化的方法,用于确保产品或服务符合预定的质量标准。在AI时代,软件质量保证的方法论需要适应不断变化的AI技术和应用场景。 3.2.1.1 质量管理体系的构建 首先,构建一个完善

AI天才研究院 1348

重谈eixt(0),ExitProcess,和TerminateProcess的区别和联系

首先来谈谈一个进程的执行流程。每个应用程序都有个主函数,在WINDOWS下,只支持两种类型的应用程序——CUI(控制台应用程序)和GUI(图形界面应用程序),相应的,其主函数类型不同。来看下这几个入口函数 int WINAPI WinMain(HINSTANCE hinstEx

酋长Clement ——成长之路 2万+

uniapp - 解决控制台提示“同步资源失败,未得到同步资源的授权,请停止运行后重新运行,并注意手机上的授权提示”,uniApp安卓/苹果App真机运行调试显示同步资源失败,无任何授权提示且无法连接

uni-app,报错,安卓Android,苹果ios,安卓app端,苹果App端,uniapp苹果安卓app,提示错误解决,HbuilderX,uniapp同步资源失败,未得到同步资源的授权,请停止运行后重新运行,并注意手机上的授权提示同步失败,安卓基座调试,没有反应,真机没有提示怎么办,如何解决这个问题,苹果ios手机,电脑模拟器运行,adb连接,连接失败,报错出错,uniapp真机运行的时候显示同步资源失败,未得到同步资源的授权,plugins里面没有tools,找不到tools,还是不行,重新打基座

🏆 前端领域优质创作者 1万+

WiFi偏门协议(四):增强的传输机制802.11aa

区分Unicast和Multicast的优先级,以及设定不同的重传次数,以细化EDCA机制。 SCS(StreamClassification Service) 如上图,在VI,VO内各自又增加了一个queue, 对应不同的MAC header。VI和VO内的两个queue可以分别对应UC和MC。这样就提供了同一个AC内的更细粒度的优先级区分。当然还可以使用Drop Eligibility Indication子域来区分不同的重传次数。 改善Multicast数据传...

reekyli的专栏 1790

windows 检查服务是否运行脚本

@echo off if "%1"=="hide" goto CmdBegin start mshta vbscript:createobject("wscript.shell").run("""%~0"" hide",0)(window.close)&&exit :CmdBegin rem 定义循环间隔时间和监测的服务: set secs=90 set srvname="Windows Update" echo. echo ===========================...

tan88881111的博客 922

uniapp(安卓 | 苹果) - 最新详细实现App打包发布教程,uni-app苹果IOS软件应用上架App Store软件商店,uni-app打包发布安卓Android软件到各大应用商店(详细教程

uni-app,安卓android,苹果ios,uniapp App软件,Uniapp上架发布应用,打包构建,apk、app软件,应用商店,应用市场,app store,uni-app安卓苹果app上架应用商店,app应用从创建到上架_uniapp打包app的流程和上架,uniapp打包上架ios Appstore 最全流程,苹果应用商店上架教程,uniapp生成的app怎么上架到iphone的app store,uni-app打包到上架Android , ios完整详细流程,审核一直不通过,软件商店审核失

✅ 精选推荐文章 1万+

C++:Windows下return,exit和ExitProcess的区别和分析

转自:http://www.cnblogs.com/jcss2008/archive/2009/03/18/1415519.html通常,我们为了使自己的程序结束,会在主函数中使用return或调用exit()。在windows下还有ExitProcess()和TerminateProcess()等函数。 本文的目的是比较以上几种结束程序的方式的区别,并分析其原理。首先我们用一个例子来说明几种结束

BAOLIANG196的博客 4247

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Windows下return,exit和ExitProcess的区别和分析

通常,我们为了使自己的程序结束,会在主函数中使用return或调用exit()。在windows下还有ExitProcess()和TerminateProcess()等函数。本文的目的是比较以上几种结束程序的方式的区别,并分析其原理。 首先我们用一个例子来说明几种结束方式的区别。测试环境为Windows XP HOME SP2,编译器为Visual Studio.net 2003测试代

黑色星辰 892

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

return, exit, exitprocess的对比

return, exit, exitprocess的对比 2007-06-15 13:32 1043人阅读 评论(1) 收藏 举报   通常,我们为了使自己的程序结束,会在主函数中使用return或调用exit()。在windows下还有ExitProcess()和TerminateProcess()等函数。 本文的目的是比较以上几种结束程序的方式的区别,并分析

电脑翻译官的专栏 468

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

exit(0),ExitProcess和ThrminateProcess的区别的联系

区别

bilibili的博客 901

exit(0),ExitProcess,和TerminateProcess的区别和联系

首先来谈谈一个进程的执行流程。每个应用程序都有个主函数,在WINDOWS下,只支持两种类型的应用程序——CUI(控制台应用程序)和GUI(图形界面应用程序),相应的,其主函数类型不同。来看下这几个入口函数[cpp] view plaincopyint WINAPI WinMain(HINSTANCE hinstExe, HINSTANCE,PSTR pszCmdLine, int nCmdShow...

zisongjia的博客 4235

结束程序的代码return 0 exit(0) ExitProcess(0)的不同之处

退出程序的几种方式 1、首先说下程序是如何启动的,前面的读书笔记已经详细写了,这儿再重复下 程序执行的步骤: A、 检索指向新进程的完整命令行的指针; B、 检索指向新进程的环境变量的指针; C、 对C/C++运行期的全局变量进程初始化; D、 对C运行期间内存单元分配函数和其他底层输入/输出例程使用的内存栈进程初始化; E、  为所有全局和静态C++类对

冷秋月的专栏 3823
上一篇: 内存管理
下一篇: 全局对象何时析构
SearchLife
博客等级 码龄20年 51粉丝 22原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值