在常见的x86调用约定中,返回适合两个寄存器的对象RDX:RAX.这是与div和mul指令的隐式输入/输出相同的寄存器对,以及cdq/ cqo(符号扩展e/rax到e/rdx).
i386 Linux(SysV)调用约定只返回64位整数.结构(甚至是由单个结构组成的结构int32_t)使用隐藏参数方法而不是打包eax或edx:eax.64位Linux,以及微软目前的标准__vectorcall,都将结构包装成e/rax,或者e/rdx:e/rax.
许多调用约定通过添加一个隐藏的额外参数来处理更大的对象:一个指向空间的指针,用于存储返回值.有关您正在使用的特定ABI,请参阅ABI文档.(链接在x86维基).
与注释中讨论的其他调用约定相比(例如,隐式使用堆栈上的空间来存储返回的大对象),传递指针可以保存副本,因为指针可以指向最终目标而不是堆栈上的临时空间.
小智 5
考虑这个程序:
struct object_t
{
int m1;
int m2;
int m3;
};
struct object_t
test1(void)
{
struct object_t o = {1, 2, 3};
return o;
}
long long
test2(void)
{
return 0LL;
}
long double
test3(void)
{
return 0.0L;
}
Run Code Online (Sandbox Code Playgroud)
在 Windows 上编译(目标文件,最少指令,没有 x87 指令):
$ gcc -Wall -c -O2 -mno-80387 test.c -o test.o
Run Code Online (Sandbox Code Playgroud)
第一个功能:
00000000 <_test1>:
0: 8b 44 24 04 mov eax,DWORD PTR [esp+0x4]
4: c7 00 01 00 00 00 mov DWORD PTR [eax],0x1
a: c7 40 04 02 00 00 00 mov DWORD PTR [eax+0x4],0x2
11: c7 40 08 03 00 00 00 mov DWORD PTR [eax+0x8],0x3
18: c3 ret
Run Code Online (Sandbox Code Playgroud)
调用者将提供一个指向他的结构在堆栈上的位置的指针作为第一个参数,test1并将使用该指针填充它。
第二个函数 ( sizeof(long long) == 8):
00000020 <_test2>:
20: 31 c0 xor eax,eax
22: 31 d2 xor edx,edx
24: c3 ret
Run Code Online (Sandbox Code Playgroud)
结果将通过两个寄存器eax和返回edx,而不仅仅是eax.
第三个函数 ( sizeof(long double) == 12):
00000030 <_test3>:
30: 31 c0 xor eax,eax
32: 31 d2 xor edx,edx
34: 31 c9 xor ecx,ecx
36: c3 ret
Run Code Online (Sandbox Code Playgroud)
返回值将通过三个寄存器传递,eax, edx, ecx。