C++ 传值,传引用,传指针区别之汇编解析

先上例子:

#include <stdio.h>

void func_reference(int & p)
{
    int l1 = 1;
    int l2 = 2;

    p = 3;

    printf("func_reference end\n");
}

void func_ptr(int * p)
{
    int l1 = 1;
    int l2 = 2;

    *p = 3;

    printf("func_ptr end\n");
}

void func_value(int p)
{
    int l1 = 1;
    int l2 = 2;

    p = 3;

    printf("func_value end\n");
}

void func_value2(int p)
{
    int l1 = 1;
    int l2 = 2;

    p++;

    printf("func_value2 end\n");
}

int main(void)
{
    int a = 5;
    int &b = a;
    int c = 6;
    int d = 7;
    int e = 8;

    func_reference(b);
    func_value(c);
    func_value2(d);
    func_ptr(&e);

    printf("a[%d], b[%d], c[%d], d[%d], e[%d]\n", a, b, c, d, e);

    return 0;
}

编译后运行:

func_reference end
func_value end
func_value2 end
func_ptr end
a[3], b[3], c[6], d[7], e[3]

 

===========================================================

                                             汇编代码

===========================================================

0000000000400640 <_Z14func_referenceRi>:
  400640:   55                      push   %rbp
  400641:   48 89 e5                mov    %rsp,%rbp
  400644:   48 83 ec 20             sub    $0x20,%rsp
  400648:   48 89 7d e8             mov    %rdi,-0x18(%rbp)
  40064c:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
  400653:   c7 45 f8 02 00 00 00    movl   $0x2,-0x8(%rbp)
  40065a:   48 8b 45 e8             mov    -0x18(%rbp),%rax
  40065e:   c7 00 03 00 00 00       movl   $0x3,(%rax)
  400664:   bf 70 08 40 00          mov    $0x400870,%edi
  400669:   e8 4a fe ff ff          callq  4004b8 <puts@plt>
  40066e:   c9                      leaveq
  40066f:   c3                      retq

0000000000400670 <_Z8func_ptrPi>:
  400670:   55                      push   %rbp
  400671:   48 89 e5                mov    %rsp,%rbp
  400674:   48 83 ec 20             sub    $0x20,%rsp
  400678:   48 89 7d e8             mov    %rdi,-0x18(%rbp)
  40067c:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
  400683:   c7 45 f8 02 00 00 00    movl   $0x2,-0x8(%rbp)
  40068a:   48 8b 45 e8             mov    -0x18(%rbp),%rax
  40068e:   c7 00 03 00 00 00       movl   $0x3,(%rax)
  400694:   bf 83 08 40 00          mov    $0x400883,%edi
  400699:   e8 1a fe ff ff          callq  4004b8 <puts@plt>
  40069e:   c9                      leaveq
  40069f:   c3                      retq

00000000004006a0 <_Z10func_valuei>:
  4006a0:   55                      push   %rbp
  4006a1:   48 89 e5                mov    %rsp,%rbp
  4006a4:   48 83 ec 20             sub    $0x20,%rsp
  4006a8:   89 7d ec                mov    %edi,-0x14(%rbp)
  4006ab:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
  4006b2:   c7 45 f8 02 00 00 00    movl   $0x2,-0x8(%rbp)
  4006b9:   c7 45 f4 03 00 00 00    movl   $0x3,-0xc(%rbp)
  4006c0:   bf 90 08 40 00          mov    $0x400890,%edi
  4006c5:   e8 ee fd ff ff          callq  4004b8 <puts@plt>
  4006ca:   c9                      leaveq
  4006cb:   c3                      retq

00000000004006cc <_Z11func_value2i>:
  4006cc:   55                      push   %rbp
  4006cd:   48 89 e5                mov    %rsp,%rbp
  4006d0:   48 83 ec 20             sub    $0x20,%rsp
  4006d4:   89 7d ec                mov    %edi,-0x14(%rbp)
  4006d7:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
  4006de:   c7 45 f8 02 00 00 00    movl   $0x2,-0x8(%rbp)
  4006e5:   83 45 ec 01             addl   $0x1,-0x14(%rbp)
  4006e9:   bf 9f 08 40 00          mov    $0x40089f,%edi
  4006ee:   e8 c5 fd ff ff          callq  4004b8 <puts@plt>
  4006f3:   c9                      leaveq
  4006f4:   c3                      retq

00000000004006f5 <main>:
  4006f5:   55                      push   %rbp
  4006f6:   48 89 e5                mov    %rsp,%rbp
  4006f9:   48 83 ec 20             sub    $0x20,%rsp
  4006fd:   c7 45 ec 05 00 00 00    movl   $0x5,-0x14(%rbp)
  400704:   48 8d 45 ec             lea    -0x14(%rbp),%rax
  400708:   48 89 45 f8             mov    %rax,-0x8(%rbp)
  40070c:   c7 45 f4 06 00 00 00    movl   $0x6,-0xc(%rbp)
  400713:   c7 45 f0 07 00 00 00    movl   $0x7,-0x10(%rbp)
  40071a:   c7 45 e8 08 00 00 00    movl   $0x8,-0x18(%rbp)
  400721:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  400725:   48 89 c7                mov    %rax,%rdi
  400728:   e8 13 ff ff ff          callq  400640 <_Z14func_referenceRi>
  40072d:   8b 45 f4                mov    -0xc(%rbp),%eax
  400730:   89 c7                   mov    %eax,%edi
  400732:   e8 69 ff ff ff          callq  4006a0 <_Z10func_valuei>
  400737:   8b 45 f0                mov    -0x10(%rbp),%eax
  40073a:   89 c7                   mov    %eax,%edi
  40073c:   e8 8b ff ff ff          callq  4006cc <_Z11func_value2i>
  400741:   48 8d 45 e8             lea    -0x18(%rbp),%rax
  400745:   48 89 c7                mov    %rax,%rdi
  400748:   e8 23 ff ff ff          callq  400670 <_Z8func_ptrPi>
  40074d:   8b 7d e8                mov    -0x18(%rbp),%edi
  400750:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  400754:   8b 10                   mov    (%rax),%edx
  400756:   8b 45 ec                mov    -0x14(%rbp),%eax
  400759:   8b 75 f0                mov    -0x10(%rbp),%esi
  40075c:   8b 4d f4                mov    -0xc(%rbp),%ecx
  40075f:   41 89 f9                mov    %edi,%r9d
  400762:   41 89 f0                mov    %esi,%r8d
  400765:   89 c6                   mov    %eax,%esi
  400767:   bf b0 08 40 00          mov    $0x4008b0,%edi
  40076c:   b8 00 00 00 00          mov    $0x0,%eax
  400771:   e8 32 fd ff ff          callq  4004a8 <printf@plt>
  400776:   b8 00 00 00 00          mov    $0x0,%eax
  40077b:   c9                      leaveq
  40077c:   c3                      retq
  40077d:   90                      nop
  40077e:   90                      nop
  40077f:   90                      nop

===========================================================

                                             传引用之汇编分析

===========================================================

 调用callq  400640 <_Z14func_referenceRi>之前的参数传递:

X64是寄存器传参,第一个参数是用rdi/edi传递,而传递给func_reference的参数为汇编指令

lea    -0x14(%rbp),%rax  获取的地址,而-0x14(%rbp)中存放的是立即数5:

movl   $0x5,-0x14(%rbp)

也就是说实际汇编传递给func_reference的是存放0x5的内存地址。

再来看func_reference的汇编指令:

0000000000400640 <_Z14func_referenceRi>:
  400640:   55                      push   %rbp
  400641:   48 89 e5                mov    %rsp,%rbp
  400644:   48 83 ec 20             sub    $0x20,%rsp
  400648:   48 89 7d e8             mov    %rdi,-0x18(%rbp)
  40064c:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
  400653:   c7 45 f8 02 00 00 00    movl   $0x2,-0x8(%rbp)
  40065a:   48 8b 45 e8             mov    -0x18(%rbp),%rax
  40065e:   c7 00 03 00 00 00       movl   $0x3,(%rax)
  400664:   bf 70 08 40 00          mov    $0x400870,%edi
  400669:   e8 4a fe ff ff          callq  4004b8 <puts@plt>
  40066e:   c9                      leaveq
  40066f:   c3                      retq

rdi中的参数最终赋给了rax, 即rax中现在存放的是传递进来的存放5的内存地址,最终:

movl   $0x3,(%rax)

把3放到rax指向的内存,也就是原来放5的内存,现在写进了3,所以最终结果是3.

===========================================================

                                             传指针之汇编分析

===========================================================

 调用callq  400670 <_Z8func_ptrPi> 之前的参数传递:

40071a:   c7 45 e8 08 00 00 00    movl   $0x8,-0x18(%rbp)

... ...

 400741:   48 8d 45 e8             lea    -0x18(%rbp),%rax
  400745:   48 89 c7                mov    %rax,%rdi

也就是说参数寄存器存放的是立即数8的内存地址。

再来看func_ptr的汇编代码:

0000000000400670 <_Z8func_ptrPi>:
  400670:   55                      push   %rbp
  400671:   48 89 e5                mov    %rsp,%rbp
  400674:   48 83 ec 20             sub    $0x20,%rsp
  400678:   48 89 7d e8             mov    %rdi,-0x18(%rbp)
  40067c:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
  400683:   c7 45 f8 02 00 00 00    movl   $0x2,-0x8(%rbp)
  40068a:   48 8b 45 e8             mov    -0x18(%rbp),%rax
  40068e:   c7 00 03 00 00 00       movl   $0x3,(%rax)
  400694:   bf 83 08 40 00          mov    $0x400883,%edi
  400699:   e8 1a fe ff ff          callq  4004b8 <puts@plt>
  40069e:   c9                      leaveq
  40069f:   c3                      retq

最终rdi中的内存地址赋值给了rax, 最后:

movl   $0x3,(%rax)

因此,内存内容被写成3

===========================================================

                                             传值之汇编分析

===========================================================

调用func_value之前的参数传递:

40070c:   c7 45 f4 06 00 00 00    movl   $0x6,-0xc(%rbp)

... ...

 40072d:   8b 45 f4                mov    -0xc(%rbp),%eax
  400730:   89 c7                   mov    %eax,%edi

因此,edi中存放的就是0x6。

再来看看func_value的汇编指令:

00000000004006a0 <_Z10func_valuei>:
  4006a0:   55                      push   %rbp
  4006a1:   48 89 e5                mov    %rsp,%rbp
  4006a4:   48 83 ec 20             sub    $0x20,%rsp
  4006a8:   89 7d ec                mov    %edi,-0x14(%rbp)
  4006ab:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
  4006b2:   c7 45 f8 02 00 00 00    movl   $0x2,-0x8(%rbp)
  4006b9:   c7 45 f4 03 00 00 00    movl   $0x3,-0xc(%rbp)
  4006c0:   bf 90 08 40 00          mov    $0x400890,%edi
  4006c5:   e8 ee fd ff ff          callq  4004b8 <puts@plt>
  4006ca:   c9                      leaveq
  4006cb:   c3                      retq

可以看到edi放到了-0x14(%rbp),但是0x3放进了 -0xc(%rbp)

再来看func_value2的汇编:

00000000004006cc <_Z11func_value2i>:
  4006cc:   55                      push   %rbp
  4006cd:   48 89 e5                mov    %rsp,%rbp
  4006d0:   48 83 ec 20             sub    $0x20,%rsp
  4006d4:   89 7d ec                mov    %edi,-0x14(%rbp)
  4006d7:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
  4006de:   c7 45 f8 02 00 00 00    movl   $0x2,-0x8(%rbp)
  4006e5:   83 45 ec 01             addl   $0x1,-0x14(%rbp)
  4006e9:   bf 9f 08 40 00          mov    $0x40089f,%edi
  4006ee:   e8 c5 fd ff ff          callq  4004b8 <puts@plt>
  4006f3:   c9                      leaveq
  4006f4:   c3                      retq

由于func_value2执行的是p++,改变的是参数本身:

 4006d4:   89 7d ec                mov    %edi,-0x14(%rbp)

... ...

 4006e5:   83 45 ec 01             addl   $0x1,-0x14(%rbp)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值