C基础----函数参数传递之值传递

    最近逛CSDN多了,看到论坛中很多新手提的问题,不禁想写个博客和大家分享一下。同时对自己也是一个复习。

    我们还是直接通过代码来讲:

  1 #include <stdio.h>
  2 
  3 int foo(int a, int b)
  4 {
  5     a = 1;
  6     b = 2;
  7 
  8     return a + b;
  9 }
 10 
 11 
 12 int main()
 13 {
 14     int a = 3;
 15     int b = 4;
 16 
 17     int c = foo(a, b);
 18 
 19     printf("a = %d, b = %d\n", a, b);
 20 
 21     return 0;
 22 }

    上面的代码很简单,函数foo的参数传递方式为传值。程序的输出为:

a = 3, b = 4
    为何foo中更改了参数a和b的值,但是main函数中输出的a和b的值却没有改变?

    是的,传值。那么传值为何不能更改他们的值呢?我们来看看生成的可执行文件反汇编的结果:

310 080483e4 <foo>:
311  80483e4:   55                      push   %ebp
312  80483e5:   89 e5                   mov    %esp,%ebp
313  80483e7:   83 ec 10                sub    $0x10,%esp
314  80483ea:   c7 45 f8 01 00 00 00    movl   $0x1,-0x8(%ebp)
315  80483f1:   c7 45 fc 02 00 00 00    movl   $0x2,-0x4(%ebp)
316  80483f8:   8b 45 fc                mov    -0x4(%ebp),%eax
317  80483fb:   8b 55 f8                mov    -0x8(%ebp),%edx
318  80483fe:   01 d0                   add    %edx,%eax
319  8048400:   c9                      leave
320  8048401:   c3                      ret
321 
322 08048402 <main>:
323  8048402:   55                      push   %ebp
324  8048403:   89 e5                   mov    %esp,%ebp
325  8048405:   83 e4 f0                and    $0xfffffff0,%esp  // 16字节对齐
326  8048408:   83 ec 20                sub    $0x20,%esp
327  804840b:   c7 44 24 14 03 00 00    movl   $0x3,0x14(%esp)
328  8048412:   00
329  8048413:   c7 44 24 18 04 00 00    movl   $0x4,0x18(%esp)
330  804841a:   00
331  804841b:   8b 44 24 18             mov    0x18(%esp),%eax  // 为foo函数调用做准备
332  804841f:   89 44 24 04             mov    %eax,0x4(%esp)
333  8048423:   8b 44 24 14             mov    0x14(%esp),%eax
334  8048427:   89 04 24                mov    %eax,(%esp)
335  804842a:   e8 b5 ff ff ff          call   80483e4 <foo>
336  804842f:   89 44 24 1c             mov    %eax,0x1c(%esp)
337  8048433:   b8 30 85 04 08          mov    $0x8048530,%eax
338  8048438:   8b 54 24 18             mov    0x18(%esp),%edx
339  804843c:   89 54 24 08             mov    %edx,0x8(%esp)
340  8048440:   8b 54 24 14             mov    0x14(%esp),%edx
341  8048444:   89 54 24 04             mov    %edx,0x4(%esp)
342  8048448:   89 04 24                mov    %eax,(%esp)
343  804844b:   e8 b0 fe ff ff          call   8048300 <printf@plt>
344  8048450:   b8 00 00 00 00          mov    $0x0,%eax
345  8048455:   c9                      leave
346  8048456:   c3                      ret
    main函数中我们只看高亮部分(后面都是为printf函数准备的)。

    这里来讲一个常识:sp和bp。

    sp都知道,存储的栈顶的位置。bp则是当前函数的栈帧的底部的位置。

    我们看看函数foo和main,最开始的部分都有:

push   %ebp
mov    %esp,%ebp
sub    $0x10,%esp

    这三行汇编的作用是建立一个新的栈帧(为这个函数!)。


    

如上图所示,call foo时,push &ebp在把ebp压栈的同时,esp也更改了。move %esp, %ebp则修改ebp为esp。其实这里就是新建了一个栈帧,栈帧的大小则是由sub $0x10, %esp决定的,即栈帧大小为16字节。

    艹,掌控不了了,写博客真是需要个本事啊...

    下面我们看看main的汇编,来看看变量的内存布局,以及参数是怎么传递给foo的。



    上图中的汇编由上到下,每步执行后的结果在栈中可以看到结果。

    上面的栈帧是main函数的,变量a、b存储在其中。在调用foo进行参数传递时,会将参数先入栈。我们发现此处入栈顺序是从右到左,b先入栈,a后入栈。在call foo时,会把"call foo"这条指令的下条指令(即"mov %eax,0x1c(%esp)")的地址压栈,以便返回时返回到这条指令。

    进入foo函数后,首先做ebp的保存,以便函数执行完成后恢复main函数的栈帧位置。然后确定foo函数的栈帧(起始位置是当前的esp,结束位置是esp -= 0x10)。

    最后就是执行“a = 1; b = 2”了。

    从汇编指令我们发现,这里其实就是创建两个局部变量:a = 1, b = 2。和传递进来的那a和b一点关系都没有,真的是一点点关系都没有!这也就是为何传值不能改变传递的参数的值的原因!


    写的有些乱,有疑问或者错误的地方可提出,谢谢!

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值