Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

函数返回值传递 #17

Open
chenpengcong opened this issue Jun 25, 2018 · 0 comments
Open

函数返回值传递 #17

chenpengcong opened this issue Jun 25, 2018 · 0 comments

Comments

@chenpengcong
Copy link
Owner

我们知道一般情况下函数的返回值是通过eax或rax寄存器来返回的,但是当我们返回值超过eax和rax所能存储的大小情况呢?

以下面代码为例,函数返回大小为32字节的结构体对象

本机环境:Linux cike-pc 4.14.0-deepin2-amd64 #1 SMP PREEMPT Deepin 4.14.12-2 (2018-01-06) x86_64 GNU/Linux

struct Test
{
    char buf[32];
};
struct Test foo()
{
    struct Test t2; 
    return t2;
}
int main()
{
    struct Test t1 = foo();
    return 0;
}

查看该文件编译后的汇编代码

$ objdump -d test.o 

test.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <foo>:
   0:    55                       push   %rbp
   1:    48 89 e5                 mov    %rsp,%rbp
   4:    48 89 7d d8              mov    %rdi,-0x28(%rbp)
   8:    48 8b 45 d8              mov    -0x28(%rbp),%rax
   c:    48 8b 55 e0              mov    -0x20(%rbp),%rdx
  10:    48 89 10                 mov    %rdx,(%rax)
  13:    48 8b 55 e8              mov    -0x18(%rbp),%rdx
  17:    48 89 50 08              mov    %rdx,0x8(%rax)
  1b:    48 8b 55 f0              mov    -0x10(%rbp),%rdx
  1f:    48 89 50 10              mov    %rdx,0x10(%rax)
  23:    48 8b 55 f8              mov    -0x8(%rbp),%rdx
  27:    48 89 50 18              mov    %rdx,0x18(%rax)
  2b:    48 8b 45 d8              mov    -0x28(%rbp),%rax
  2f:    5d                       pop    %rbp
  30:    c3                       retq   

0000000000000031 <main>:
  31:    55                       push   %rbp
  32:    48 89 e5                 mov    %rsp,%rbp
  35:    48 83 ec 20              sub    $0x20,%rsp
  39:    48 8d 45 e0              lea    -0x20(%rbp),%rax
  3d:    48 89 c7                 mov    %rax,%rdi
  40:    b8 00 00 00 00           mov    $0x0,%eax
  45:    e8 00 00 00 00           callq  4a <main+0x19>
  4a:    b8 00 00 00 00           mov    $0x0,%eax
  4f:    c9                       leaveq 
  50:    c3                       retq

汇编代码的意义如下

首先看main函数,函数main在栈上开辟0x20大小的空间,并将栈顶的地址赋值给rdi寄存器(作为传递给foo的第一个参数)。

  35:    48 83 ec 20              sub    $0x20,%rsp
  39:    48 8d 45 e0              lea    -0x20(%rbp),%rax
  3d:    48 89 c7                 mov    %rax,%rdi

接下来看foo函数,函数foo在栈上开辟0x28大小的空间,并将栈顶(8个字节)的值赋值为%rdi(即main函数传递进来的地址)。然后将该栈上剩下的0x20大小的内存数据对应复制到main函数开辟的0x20大小的栈空间。

   4:    48 89 7d d8              mov    %rdi,-0x28(%rbp)
   8:    48 8b 45 d8              mov    -0x28(%rbp),%rax
   c:    48 8b 55 e0              mov    -0x20(%rbp),%rdx
  10:    48 89 10                 mov    %rdx,(%rax)
  13:    48 8b 55 e8              mov    -0x18(%rbp),%rdx
  17:    48 89 50 08              mov    %rdx,0x8(%rax)
  1b:    48 8b 55 f0              mov    -0x10(%rbp),%rdx
  1f:    48 89 50 10              mov    %rdx,0x10(%rax)
  23:    48 8b 55 f8              mov    -0x8(%rbp),%rdx
  27:    48 89 50 18              mov    %rdx,0x18(%rax)

可以这样理解上述的执行过程:

  1. main函数新建一个struct Test类型变量t1,并将t1的地址作为隐藏参数传递给foo函数
  2. foo函数新建一个struct Test类型变量t2,并将t2的内容拷贝给t1

由于t2的内容已经拷贝到t1了,所以t1就相当于foo函数返回的t2

从上面分析来看,其实foo函数的声明struct Test foo()更像是void foo(struct Test *ret)

@chenpengcong chenpengcong changed the title 函数返回值传递.md 函数返回值传递 Feb 7, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant