鱼C论坛

 找回密码
 立即注册
查看: 4414|回复: 3

[技术交流] return 和 exit 有什么不同之处?

[复制链接]
发表于 2012-3-9 12:37:43 | 显示全部楼层 |阅读模式
20鱼币
  1. #include <stdio.h>

  2. #define ERROR 1

  3. int main(int argc)
  4. {
  5.         int fishc_b;
  6.        
  7.         if( agrc < 3 )
  8.         {
  9.                 printf("Wrong number of arguments used!\n");
  10.                 exit(1);   
  11.         }
  12.        
  13.         fishc_b = do_fishc(0);
  14.        
  15.         ... // 省略
  16.        
  17.         return 0;
  18. }

  19. do_fishc(int)
  20. {
  21.         int fishc_b;
  22.        
  23.         fishc_b = do_fishc_a();
  24.         if( fishc_b = ERROR )
  25.         {
  26.                 printf("Something ERROR!\n");
  27.                 exit(0);
  28.         }
  29.        
  30.         return 0;
  31. }

  32. do_fishc_a()
  33. {
  34.         return 1;
  35. }
复制代码




                               
登录/注册后可看大图
该贴已经同步到 小甲鱼的微博

最佳答案

查看完整内容

基本就如2楼说的,更深入的说明如下: 通常,我们为了使自己的程序结束,会在主函数中使用return或调用exit()。 本文的目的是比较以上两种结束程序的方式的区别,并分析其原理。 首先我们用一个例子来说明几种结束方式的区别。 测试环境为Windows 7旗舰版,编译器为Visual Studio.net 2010(VC6在这个程序上表现不符合标准) #include #include using namespace std; class Test{ public: Test (int d):data(d) ...
小甲鱼最新课程 -> https://ilovefishc.com
发表于 2012-3-9 12:37:44 | 显示全部楼层
基本就如2楼说的,更深入的说明如下:
通常,我们为了使自己的程序结束,会在主函数中使用return或调用exit()。
本文的目的是比较以上两种结束程序的方式的区别,并分析其原理。

首先我们用一个例子来说明几种结束方式的区别。
测试环境为Windows 7旗舰版,编译器为Visual Studio.net 2010(VC6在这个程序上表现不符合标准)

#include <iostream>
#include <cstdlib>
using namespace std;

class Test{
public:
        Test (int d):data(d) { cout<<"construct:"<<data<<endl; }
        ~Test () { cout<<"destruct:"<<data<<endl; }
private:
        int data;
};

Test t_1 (1);

int main( ) {
        Test t_2 (2);
        printf("Hello World!\n");
        // return 0;
        exit (0);
}

程序在运行的结果为:

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

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

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

为什么会出现这样的情况呢?
《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结束进程。
所以,exit只负责析构全局对象,return 0可以析构局部对象并调用exit,因此能析构全部对象。
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2012-3-9 14:34:09 | 显示全部楼层
return 表示从被调函数返回到主调函数继续执行
exit(0) 表示程序正常退出, exit(1)/exit(-1)表示程序异常退出
exit() 结束当前进程/当前程序/,在整个程序中,只要调用 exit ,就结束
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2012-3-10 19:52:18 | 显示全部楼层
希望小甲鱼的网站登录那个地方可以修改一下。只要输完用户名,按下回车键,因为没输入密码,但是没有聚焦到密码框。一般用户的操作是密码错误。所以希望这个小细节可以改正。
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-9-21 02:38

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表