return和exit

return和exit

标签(空格分隔): APUE


  在每个函数结束之后,习惯上都会return一下。而在main函数里面,既可以用return,也可以用exit来结束进程。现在来看看return到底做了什么。
  先写一个简单的C程序:

#include<stdio.h>


int add(int ,int);

int main(){
    int a = 1;
    int b = 2;
    int c = add(a, b);
    return 1;   
}



int add(int a, int b){

    int c =a + b;
    return c;
}

然后反汇编,看看汇编的东西:

080483ed <main>:
 80483ed:   55                      push   %ebp
 80483ee:   89 e5                   mov    %esp,%ebp
 80483f0:   83 e4 f0                and    $0xfffffff0,%esp
 80483f3:   83 ec 20                sub    $0x20,%esp
 80483f6:   c7 44 24 14 01 00 00    movl   $0x1,0x14(%esp)
 80483fd:   00 
 80483fe:   c7 44 24 18 02 00 00    movl   $0x2,0x18(%esp)
 8048405:   00 
 8048406:   8b 44 24 18             mov    0x18(%esp),%eax
 804840a:   89 44 24 04             mov    %eax,0x4(%esp)
 804840e:   8b 44 24 14             mov    0x14(%esp),%eax
 8048412:   89 04 24                mov    %eax,(%esp)
 8048415:   e8 0b 00 00 00          call   8048425 <add>
 804841a:   89 44 24 1c             mov    %eax,0x1c(%esp)
 804841e:   b8 00 00 00 00          mov    $0x0,%eax
 8048423:   c9                      leave  
 8048424:   c3                      ret    

08048425 <add>:
 8048425:   55                      push   %ebp
 8048426:   89 e5                   mov    %esp,%ebp
 8048428:   83 ec 10                sub    $0x10,%esp
 804842b:   8b 45 0c                mov    0xc(%ebp),%eax
 804842e:   8b 55 08                mov    0x8(%ebp),%edx
 8048431:   01 d0                   add    %edx,%eax
 8048433:   89 45 fc                mov    %eax,-0x4(%ebp)
 8048436:   8b 45 fc                mov    -0x4(%ebp),%eax
 8048439:   c9                      leave  
 804843a:   c3                      ret        

其他的不用管,只要看两个程序的结尾,可以看到,两个程序最后面的内容。后面如果是有返回值的return,则相当于如下三条指令:

804841e:    b8 00 00 00 00          mov    $0x0,%eax
 8048423:   c9                      leave  
 8048424:   c3                      ret    

第一条指令是给eax寄存器附上返回值,后面的两条指令,相当于:

mov %ebp,%esp

pop %ebp

pop %rip

稍微有一点寄存器基础的,就知道,这三条指令是函数栈的回收。即return就是函数栈的回收指令。

下面再来看下exit。
和上面一样的程序,只是在main里面用exit(0)来退出程序。反汇编得到的汇编如下:

0804841d <main>:
 804841d:   55                      push   %ebp
 804841e:   89 e5                   mov    %esp,%ebp
 8048420:   83 e4 f0                and    $0xfffffff0,%esp
 8048423:   83 ec 20                sub    $0x20,%esp
 8048426:   c7 44 24 14 01 00 00    movl   $0x1,0x14(%esp)
 804842d:   00 
 804842e:   c7 44 24 18 02 00 00    movl   $0x2,0x18(%esp)
 8048435:   00 
 8048436:   8b 44 24 18             mov    0x18(%esp),%eax
 804843a:   89 44 24 04             mov    %eax,0x4(%esp)
 804843e:   8b 44 24 14             mov    0x14(%esp),%eax
 8048442:   89 04 24                mov    %eax,(%esp)
 8048445:   e8 10 00 00 00          call   804845a <add>
 804844a:   89 44 24 1c             mov    %eax,0x1c(%esp)
 804844e:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
 8048455:   e8 a6 fe ff ff          call   8048300 <exit@plt>

可以看到和上面不一样的是:

 804844e:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
 8048455:   e8 a6 fe ff ff          call   8048300 <exit@plt>

他把esp所指向的地址里面的内容改为了0,然后调用exit。这里和上面的return不一样的是,他没有摧毁栈,然后返回上一级函数。
  而exit()的作用是调用_exit()或者_Exit()系统调用,来直接结束进程。
  
  再来说下atexit()函数。
  int atexit(void (*func) (void));
  通俗来说,atexit函数是一个终止处理程序,他登记那些想在退出main函数之后,再执行的函数,这些函数会在exit()函数中被调用。这里要注意两点:1.登记的函数只能是无输入无输出的函数。2.最后在exit函数中,调用终止处理程序的顺序和登记的时候是相反的。
  关于第二点函数执行顺序的原因说一下自己的猜想:感觉应该是在atexit函数执行的时候,只是把函数指针压入exit的函数栈里面,所以根据栈的特性,最后的执行顺序和压入顺序是反的。
  看一下例子:

#include<stdio.h>
#include<stdlib.h>

void print1(){
    printf("exit1\n");
    }

void print2()
{
    printf("exit2\n");
}
int main(){
    atexit(print1);
    atexit(print1);
    atexit(print2); 
    return 0;
}

结果是:
此处输入图片的描述

可以看到他的执行顺序是反过来的。而且注意,在C程序里面,最后的返回是用的return而不是exit。而终止处理程序实在exit函数里面掉用的。所以简单来说,在main函数里面return到了main函数的上层函数,在上层函数里面依旧调用了exit函数。

总结return和exit:
return只是一个销毁函数栈的指令,在销毁函数栈之后,会调到调用函数中去。而exit是三个作用:执行终止处理程序(注意执行顺序),清理标准I/O,调用_exit或者_Exit系统调用,结束进程。
而在非main函数中,一般使用return,因为exit直接就销毁进程了。
在main函数中,return会把main的栈给销毁掉,在回到上层调用函数,在上层调用函数里面会执行exit()函数来销毁进程。exit就不会销毁main的栈,而是直接就销毁进程。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值