如果struct不大于一个register大小,直接保存在%rax。
如果struct在1~2个register之间,一般保存在%rax和%rdx。
大于2个register的情况,一般是calling function传一个隐藏的指针给called function,然后后者往指针处赋值。
但这是依赖于编译器的,不同编译器实现可能不一样。
所以如果是动态链接库,是坚决不鼓励返回一个struct的。
下面看一个具体的例子:
#include<stdio.h>
typedef struct {
long i;
long j;
char c;
} t_test;
t_test func()
{
t_test t;
t.i = 100;
t.j = 90;
t.c = 'a';
return t;
}
int main(int arg, char* argv[]){
t_test a;
a = func();
printf("a.i=%ld\na.j=%ld\na.c=%c\n", a.i, a.j, a.c);
return 0;
}
先看calling function:
0x00000000004004db <main+15>: lea -0x20(%rbp),%rdi
0x00000000004004df <main+19>: mov $0x0,%eax
0x00000000004004e4 <main+24>: callq 0x400498 <func>
果然是通过%rdi传了个隐藏的指针,指向-0x20(%rbp)。
再看called function:
Dump of assembler code for function func:
0x0000000000400498 <func+0>: push %rbp
0x0000000000400499 <func+1>: mov %rsp,%rbp
0x000000000040049c <func+4>: movq $0x64,-0x20(%rbp)
0x00000000004004a4 <func+12>: movq $0x5a,-0x18(%rbp)
0x00000000004004ac <func+20>: movb $0x61,-0x10(%rbp)
0x00000000004004b0 <func+24>: mov -0x20(%rbp),%rax
0x00000000004004b4 <func+28>: mov %rax,(%rdi)
0x00000000004004b7 <func+31>: mov -0x18(%rbp),%rax
0x00000000004004bb <func+35>: mov %rax,0x8(%rdi)
0x00000000004004bf <func+39>: mov -0x10(%rbp),%rax
0x00000000004004c3 <func+43>: mov %rax,0x10(%rdi)
0x00000000004004c7 <func+47>: mov %rdi,%rax
0x00000000004004ca <func+50>: leaveq
0x00000000004004cb <func+51>: retq
End of assembler dump.
也是一致的,先把struct拷贝到%rdi指向的内存区域,再把%rdi赋值给%rax。
在动态库中直接返回struct要尽量避免:)
注意:
指令集(Instruction Set Architecture)对内存的读、写总是指定的单元的低地址(上升地址),e.g. mov %rax,0x10(%rdi)操作的内存范围是0x10(%rdi)~0x18(%rdi)