C++:Windows下return,exit和ExitProcess的区别和分析
来源:互联网 发布:互联网金融 中国 知乎 编辑:程序博客网 时间:2024/05/16 14:28
转自:http://www.cnblogs.com/jcss2008/archive/2009/03/18/1415519.html
通常,我们为了使自己的程序结束,会在主函数中使用return或调用exit()。在windows下还有ExitProcess()和TerminateProcess()等函数。
本文的目的是比较以上几种结束程序的方式的区别,并分析其原理。
首先我们用一个例子来说明几种结束方式的区别。
测试环境为Windows XP HOME SP2,编译器为Visual Studio.net 2003
测试代码如下:
#include#include#includeclass 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结束进程。
- Windows下return,exit和ExitProcess的区别和分析- -
- Windows下return,exit和ExitProcess的区别和分析
- Windows下return,exit和ExitProcess的区别和分析
- Windows下return,exit和ExitProcess的区别和分析
- Windows下return,exit和ExitProcess的区别和分析- -
- Windows下return,exit和ExitProcess的区别和分析
- Windows下return,exit和ExitProcess的区别和分析
- Windows下return,exit和ExitProcess的区别和分析- -
- C++:Windows下return,exit和ExitProcess的区别和分析
- exit、ExitProcess和TerminateProcess 的区别
- c中exit和return的区别
- exit(0),ExitProcess和ThrminateProcess的区别的联系
- return, exit, exitprocess的对比
- Exit() 和 Return() 的区别
- exit()和return()的区别
- return和exit的区别
- return和 exit的区别
- exit()和return()的区别
- 常量的复用
- PyTorch(总)——PyTorch遇到令人迷人的BUG与记录
- opencv-python 基本例子3个---显示图片,绘制图形,人脸识别
- idea初使用之自动编译
- iOS开发基础pch设置
- C++:Windows下return,exit和ExitProcess的区别和分析
- 半双工串口
- HashMap的源码分析
- JavaScript代码应该放在HTML不同位置的区别
- golang在linux和windows下的安装以及环境变量的配置
- xcode智能提示失效
- Android 自定义View 之 圆形头像
- Navicat中MySQL server has gone away错误怎么办
- 【ActiveMQ】ActiveMQ在Windows的安装,以及点对点的消息发送案例