C 如何返回结构体?
(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
,但是这种工作保证可以工作吗?返回时不是不应该引用任何局部变量吗?
(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;
}
So it seems it's returning the local variable t
,but is this kind of job guaranteed to work, isn't it supposed not to refer to any local variables when return??
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
根据我的经验,C 返回结构没有标准方法。为了能够传递结构,编译器通常(对用户不可见)传递指向结构的指针,函数可以将内容复制到该结构。如何传递此指针(堆栈上的第一个或最后一个)取决于实现。某些编译器(例如 32 位 MSVC++)会在 EAX 和 EDX 等寄存器中返回小型结构。显然,GCC 在 64 位模式下在 RAX 中返回这样的结构。
但是,再次强调,没有标准的方法来实现这一点。当使用某个函数的其余代码也由同一编译器编译时,这没有问题,但如果该函数是 DLL 或 lib 的导出函数,那就有问题了。当我使用来自不同语言(Delphi)或来自不同编译器的 C 的此类函数时,我已经被这个问题困扰过几次。请参阅此链接< /a> 也是。
In my experience, there is no standard way how C returns a structure. To be able to pass a struct, the compiler usually (invisibly to the user) passes a pointer to the struct, to which the function can copy the contents. How this pointer is passed (first or last on stack) is implementation dependent. Some compilers, like 32 bit MSVC++ return small structures in registers like EAX and EDX. Apparently, GCC returns such a struct in RAX, in 64 bit mode.
But, once again, there is no standard way how this is done. That is no problem when the rest of the code using a function is also compiled by the same compiler, but it is a problem if the function is an exported function of a DLL or a lib. I have been bitten by this a few times, when using such functions from a different language (Delphi) or from C with a different compiler. See this link too.
RAX 足够大,可以容纳整个结构。在 0x00000000004004c7 处,您正在加载整个结构(使用 mov),而不是其地址(您可以使用 lea 代替)。
x86-64 System V ABI 的调用约定在 RDX:RAX 或 RAX 中返回最多 16 个字节的 C 结构。 x86-64 上的 C++:结构/类何时在寄存器中传递和返回?
对于较大的结构,调用者会传递一个“隐藏”输出指针 arg。
RAX is big enough to hold the entire structure. At 0x00000000004004c7 you're loading the entire structure (with mov), not its address (you'd use lea instead).
The x86-64 System V ABI's calling convention returns C structs up to 16 bytes in RDX:RAX or RAX. C++ on x86-64: when are structs/classes passed and returned in registers?
For larger structs, the there's a "hidden" output pointer arg passed by the caller.
东西的返回方式根本不是标准的,但通常是在 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 约定),返回值指针通常作为隐藏的第二个参数而不是第一个参数传递。
It's not really standard at all how things are returned, but it's usually in RAX. In your example, assuming t_test::i and t_test::c are the only members of t_test and are at most 32-bits each, the entire structure can fit into a 64-bit register, so it just returns the values directly through RAX, and usually things that can fit into 2 registers are returned in RAX:RDX (or RDX:RAX, I forget the common order).
For larger than two registers, it generally involves a hidden pointer parameter being passed as the first parameter, which points to an object in the calling function (usually the one that directly gets assigned the return value). This object is then written to before returning from the called function (usually copied from the local structure used in the called function), and usually the same pointer that was passed is returned in RAX.
EAX/EDX can be substituted in for RAX/RDX on 32-bit x86 systems.
With conventions that pass the "this" pointer on the stack (like standard x86 GCC conventions), the return value pointer is usually passed as a hidden second parameter, instead of the first.
您的原始代码返回在函数中创建的结构的副本 - 因为您返回的是结构类型,而不是指向结构的指针。看起来整个结构都是通过 rax 值传递的。一般来说,编译器可以为此生成各种汇编代码,具体取决于调用者和被调用者的行为以及调用约定。
处理结构的正确方法是将它们用作输出参数:
Your original code is returning a copy of the structure created in the function - because you're returning a structure type, not a pointer to a structure. What it looks like is that the entire structure is passed by value with
rax
. Generally speaking, the compiler can produce various assembly codes for this, depending and caller and callee behavior and calling convention.The proper way to handle structure is to use them as out parameters:
堆栈指针在函数开始时不会更改,因此
t_test
的分配不是在函数内进行的,因此不会被函数释放。如何处理取决于所使用的调用约定。如果您查看该函数是如何调用的,就可以更容易地了解它是如何完成的。The stack pointer isn't changed in the start of the function, so the allocation of
t_test
isn't made within the function, and thus not freed by the function. How this is handled depends on the calling convention used. If you look at how the function is called it can be easier to see how it is done.