How does C return a structure?

(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; ti = 100; tc = 'a'; return t; } 

So, it seems that it returns a local variable t , but is this kind of work guaranteed, is it supposed to refer to any local variables when returning?

+4
source share
5 answers

In my experience, there is no standard way how C returns a structure. To be able to pass the structure, the compiler usually (invisible to the user) passes a pointer to the structure by which the function can copy the contents. How this pointer is passed (first or last on the stack) depends on the implementation. Some compilers, such as 32-bit MSVC ++, return small structures to registers like EAX and EDX. Apparently, GCC returns such a structure in RAX in 64-bit mode.

But, again, there is no standard way to do this. This is not a problem when the rest of the code using the function is also compiled by the same compiler, but it is a problem if the function is an exported function of the DLL or lib. I was bitten by this several times when I used such functions from another language (Delphi) or from C with another compiler. See this link .

+5
source

perhaps rax is large enough to hold the entire structure. At 0x00000000004004c7 you get the whole structure (with mov), not its address (you use lea instead)

+4
source

This is not quite standard, as everything returns, but usually it is in RAX. In your example, if t_test :: i and t_test :: c are the only members of t_test and no more than 32 bits, the whole structure can fit into a 64-bit register, so it just returns values ​​directly through RAX, and usually things that can fit in 2 registers, return to RAX: RDX (or RDX: RAX, I forget the general order).

For more than two registers, the hidden pointer parameter is usually used, passed as the first parameter that points to the object in the calling function (usually the one to which the return value is directly assigned). This object is then written 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 to RAX.

EAX / EDX can be replaced with RAX / RDX on x86 32-bit systems.

With conventions that pass the "this" pointer on the stack (for example, standard x86 GCC conventions), the return pointer is usually passed as a hidden second parameter, not the first.

+3
source

A copy of the structure created in the function is returned in the source code because you are returning the type of the structure, not a pointer to the structure. It looks like the whole structure is passed by value using rax . Generally speaking, the compiler can create various assembly codes for this, depending on the behavior of the caller and the callee and the calling agreement.

The correct way to handle the structure is to use them as parameters:

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

The stack pointer does not change at the beginning of the function, so t_test allocation t_test not performed inside the function and, therefore, is not freed by the function. How this is handled depends on the calling convention used. If you look at how a function is called, it may be easier to see how this is done.

0
source

All Articles