C如何返回结构?

(gdb) disas func Dump of assembler code for function func: 0x00000000004004b8 : push %rbp 0x00000000004004b9 : mov %rsp,%rbp 0x00000000004004bc : movl $0x64,0xfffffffffffffff0(%rbp) 0x00000000004004c3 : movb $0x61,0xfffffffffffffff4(%rbp) 0x00000000004004c7 : mov 0xfffffffffffffff0(%rbp),%rax 0x00000000004004cb : leaveq 0x00000000004004cc : retq End of assembler dump. t_test func() { t_test t; ti = 100; tc = 'a'; return t; } 

所以它似乎正在返回局部变量t ,但是这种工作保证可以工作,是不是应该在返回时不引用任何局部变量?

根据我的经验,C返回结构没有标准的方法。 为了能够传递结构,编译器通常(不可见地向用户)传递指向结构的指针,函数可以将结构复制到该结构中。 如何传递此指针(堆栈中的第一个或最后一个)是依赖于实现的。 一些编译器,如32位MSVC ++,在EAX和EDX等寄存器中返回小结构。 显然,GCC以64位模式在RAX中返回这样的结构。

但是,再一次,没有标准的方法如何做到这一点。 当使用函数的其余代码也由同一编译器编译时,这没有问题,但如果函数是DLL或lib的导出函数则是一个问题。 当使用来自不同语言(Delphi)的这些函数或使用不同编译器的C时,我已经被这几次咬过了。 也看到这个链接 。

也许rax足以容纳整个结构。 在0x00000000004004c7你得到整个结构(用mov),而不是它的地址(你用lea代替)

事情的回归并不是真正的标准,但通常是在RAX中。 在您的示例中,假设t_test :: i和t_test :: c是t_test的唯一成员,并且每个最多32位,整个结构可以适合64位寄存器,因此它只是直接通过RAX返回值通常可以放入2个寄存器的东西在RAX中返回:RDX(或RDX:RAX,我忘记了常用顺序)。

对于大于两个寄存器,它通常涉及作为第一个参数传递的隐藏指针参数,该参数指向调用函数中的对象(通常是直接获得指定返回值的对象)。 然后在从被调用函数返回之前写入该对象(通常从被调用函数中使用的本地结构复制),并且通常在RAX中返回传递的相同指针。

在32位x86系统上,EAX / EDX可替代RAX / RDX。

对于在堆栈上传递“this”指针的约定(类似于标准的x86 GCC约定),返回值指针通常作为隐藏的第二个参数传递,而不是第一个参数。

您的原始代码返回函数中创建的结构的副本 – 因为您返回的是结构类型,而不是指向结构的指针。 看起来是整个结构通过rax传递的值。 一般来说,编译器可以为此生成各种汇编代码,具体取决于调用者和被调用者行为以及调用约定。

处理结构的正确方法是将它们用作out参数:

 void func(t_test* t) { t->i = 100; t->c = 'a'; } 

堆栈指针在函数的开头没有改变,因此t_test的分配不在函数内部,因此不被函数释放。 如何处理它取决于使用的调用约定。 如果你看一下如何调用函数,可以更容易地看到它是如何完成的。