(gdb) disas func
Dump of assembler code for function func:
0x00000000004004b8 <func+0>: push %rbp
0x00000000004004b9 <func+1>: mov %rsp,%rbp
0x00000000004004bc <func+4>: movl $0x64,0xfffffffffffffff0(%rbp)
0x00000000004004c3 <func+11>: movb $0x61,0xfffffffffffffff4(%rbp)
0x00000000004004c7 <func+15>: mov 0xfffffffffffffff0(%rbp),%rax
0x00000000004004cb <func+19>: leaveq
0x00000000004004cc <func+20>: retq
End of assembler dump.
t_test func()
{
t_test t;
t.i = 100;
t.c = 'a';
return t;
}
所以它似乎正在返回局部变量t
,但是这种工作保证可以正常工作,是不是应该在返回时不引用任何局部变量?
答案 0 :(得分:5)
根据我的经验,C返回结构没有标准方法。为了能够传递结构,编译器通常(不可见地向用户)传递指向结构的指针,函数可以将结构复制到该结构中。如何传递此指针(堆栈中的第一个或最后一个)是依赖于实现的。一些编译器,如32位MSVC ++,在EAX和EDX等寄存器中返回小结构。显然,GCC以64位模式在RAX中返回这样的结构。
但是,再一次,没有标准的方法来完成这项工作。当使用函数的其余代码也由同一编译器编译时,这没有问题,但如果函数是DLL或lib的导出函数则是一个问题。当使用来自不同语言(Delphi)的这些函数或使用不同编译器的C时,我已经被这几次咬过了。另见this link。
答案 1 :(得分:4)
也许rax足以容纳整个结构。在0x00000000004004c7你得到整个结构(用mov),而不是它的地址(你用lea代替)
答案 2 :(得分:3)
根本不是标准回归的标准,但通常是在RAX中。在您的示例中,假设t_test :: i和t_test :: c是t_test的唯一成员,并且每个最多为32位,整个结构可以适合64位寄存器,因此它只是通过RAX直接返回值,通常可以放入2个寄存器的东西在RAX中返回:RDX(或RDX:RAX,我忘记了常用顺序)。
对于大于两个寄存器,它通常涉及作为第一个参数传递的隐藏指针参数,该参数指向调用函数中的对象(通常是直接获得指定返回值的对象)。然后在从被调用函数返回之前写入该对象(通常从被调用函数中使用的本地结构复制),并且通常在RAX中返回相同的指针。
EAX / EDX可替代32位x86系统上的RAX / RDX。
使用在堆栈上传递“this”指针的约定(类似于标准的x86 GCC约定),返回值指针通常作为隐藏的第二个参数传递,而不是第一个参数。
答案 3 :(得分:1)
您的原始代码返回函数中创建的结构的副本 - 因为您返回的是结构类型,而不是指向结构的指针。看起来是整个结构通过值rax
传递。一般来说,编译器可以为此生成各种汇编代码,具体取决于调用者和被调用者行为以及调用约定。
处理结构的正确方法是将它们用作输出参数:
void func(t_test* t)
{
t->i = 100;
t->c = 'a';
}
答案 4 :(得分:0)
堆栈指针在函数的开头没有改变,因此t_test
的分配不在函数内部,因此不被函数释放。如何处理它取决于使用的调用约定。如果你看一下如何调用函数,可以更容易地看到它是如何完成的。