50

How does a function return value is clear to me, just to kick start:

int f()
{
  int a = 2;
  return a;
}

Now a gets the memory in stack and its life-span is within the f() in order to return the value it copies the value to a special register which is read by the caller as it knows that the callee have placed the value for him. (Since the size of return-value-holder special register size is limited that's why we cant return large objects therefore In case of advance languages when we want to return object function actually copies the address of object in heap to that special register)

Lets come back to C for a situation when i want to return a struct variable not pointer:

struct inventory
{
    char name[20];
    int number;
};
struct inventory function();

int main()
{
    struct inventory items;
    items=function();
    printf("\nam in main\n");
    printf("\n%s\t",items.name);
    printf(" %d\t",items.number); 
    getch();
    return 0;
}

struct inventory function()
{
    struct inventory items;
    printf(" enter the item name\n ");
    scanf(" %s ",&items.name );
    printf(" enter the number of items\n ");
    scanf("%d",&items.number );
    return items;
}

Code forked from: https://stackoverflow.com/a/22952975/962545

Here is the deal,

Lets start with main, items variable declared but not initialized and then function is called which return initialized structure variable which gets copied to the one in main. Now I am bit blurred to understand how function() returned struct variable items which is not dynamically created(technically not in heap) so this variable's life-span is within the function() body, also size of variable item can be huge enough not to fit in special register so why it worked?.(I know we can dynamically allocate item inside function and return the address but I don't want alternative, I am looking for explanation)

Question: Although it works but how does function() actually returned the struct variable and get copied to items variable in main when it is supposed to die with function() return.

I am surely missing important thing, detailed explanation would help. :)

EDIT: Other Answer References:

  1. https://stackoverflow.com/a/2155742/962545
  2. Named as return value optimization
Community
  • 1
  • 1
gitesh.tyagi
  • 2,271
  • 1
  • 14
  • 21
  • 8
    The standard does not define how this is done. Different compilers can, and do, choose different ways to implement return values. You need to read the ABI for the compiler you are using. Semantically, a copy is passed from callee to caller. – David Heffernan Apr 09 '14 at 08:52
  • Yes i agree copy will be passed and I'm more interested in digging into the travelling path of that copy. – gitesh.tyagi Apr 09 '14 at 08:56
  • 2
    If you compile the code with the option to keep the intermediate .ASM file for your machine you can look at that code to see exactly what is going on. – AnthonyLambert Apr 09 '14 at 08:56
  • 3
    possible duplicate of [How do C compilers implement functions that return large structures?](http://stackoverflow.com/questions/2155730/how-do-c-compilers-implement-functions-that-return-large-structures) – unwind Apr 09 '14 at 08:59
  • Look at the assembly code generated by your compiler to get an idea how **your** compiler handles this. Another compiler may handle it differently though. – Jabberwocky Apr 09 '14 at 09:02
  • 1
    `main holds is actually an illegal pointer` but there are no pointers. `main_a` is not a pointer – KamilCuk Oct 21 '20 at 23:23
  • 3
    It's OK to return a copy of a local variable — you do it all the time with functions that return an `int`. What's not OK is trying to return a pointer to a local variable; a copy of the pointer is returned OK, but the data it points at no longer exists, leading to problems. – Jonathan Leffler Oct 21 '20 at 23:30
  • @JonathanLeffler yes I know that, thats why I was asking why people say it is ok to return a struct without using malloc –  Oct 21 '20 at 23:31
  • 2
    Because you're returning a copy of the struct, not a pointer to the struct. Like you return a copy of an `int`, not a pointer to an `int`. – Jonathan Leffler Oct 21 '20 at 23:31
  • @JonathanLeffler sorry I don't get it, do you mean the `foo` fucntion return a copy of the struct by writting values in `main` method 's stack where `main_a` is? –  Oct 22 '20 at 02:32
  • More or less, yes. The implementation-level details are up to the implementation, but the net result is that a copy of the structure is made available to the calling function (`main()`) and is assigned to `main_a`. It might be that the compiler passes a pointer to `main_a` to the function and the function copies the local variable over the variable pointed to — but there could be other ways of implementing it too. At one level, all that matters is that the C standard says you can return structures, and the compiler simply has to implement that somehow. – Jonathan Leffler Oct 22 '20 at 03:51
  • A function can always return its own type. – David C. Rankin Oct 24 '20 at 04:29

4 Answers4

42

Details vary widely by calling convention. Some ABIs have no calling convention for passing whole structures, in which case the compiler is free to do whatever it thinks makes sense.

Examples include:

  • Passing and returning the entire struct as a series of consecutive registers (often used with "small" structs)
  • Placing the entire struct as an argument block on the stack
  • Allocating an empty argument big enough to hold the struct, to be filled with a return value
  • Passing the (stack) address of the struct as an argument (as if the function was declared void function(struct inventory *))

Any of these implementations could conform to the C spec here. But, let's look at a specific implementation: the output from my GCC ARM cross-compiler.

Compiling the code you gave gives me this:

main:
    stmfd   sp!, {fp, lr}
    add fp, sp, #4
    sub sp, sp, #48
    sub r3, fp, #52
    mov r0, r3
    bl  function(PLT)

Destination operands are always on the left. You can see that the program reserves stack space, then passes the address of the stack space as r0 (the first argument in the ARM EABI calling convention). function takes no arguments, so this argument is clearly an artificial argument added by our compiler.

function looks like this:

function:
    stmfd   sp!, {r4, fp, lr}
    add fp, sp, #8
    sub sp, sp, #36
    str r0, [fp, #-40]
    ldr r3, .L6

        ...
    add r2, pc, r2
    mov r0, r2
    mov r1, r3
    bl  scanf(PLT)
    ldr r3, [fp, #-40]
    mov ip, r3
    sub r4, fp, #36
    ldmia   r4!, {r0, r1, r2, r3}
    stmia   ip!, {r0, r1, r2, r3}
    ldmia   r4, {r0, r1}
    stmia   ip, {r0, r1}
    ldr r0, [fp, #-40]
    sub sp, fp, #8
    ldmfd   sp!, {r4, fp, pc}

This code basically stashes the single argument in [fp, #-40], then later loads it and begins stashing data at the address it points to. At the end, it returns this pointer value in r0 again. Effectively, the compiler has made the function signature into

struct inventory *function(struct inventory *)

where the returned structure is allocated on the stack by the caller, passed in, and then returned.

nneonneo
  • 171,345
  • 36
  • 312
  • 383
12

You're missing the most obvious thing there is to C's way of passing/returning things: everything is passed around by value, or at least: it behaves that way.

That is to say:

struct foo some_f( void )
{
    struct foo local = {
       .member = 123,
       .bar = 2.0
    };
    //some awsome code
    return local;
}

Will work, just fine. If the struct is small, then it's possible that this code will create a local struct variable, and return a copy of that struct to the caller.
In other cases, however, this code will roughly translate to :

void caller()
{
    struct foo hidden_stack_space;
    struct foo your_var = *(some_f(&hidden_stack_space));
}
//and the some_f function will behave as:
struct foo * some_f(struct foo * local)
{
    //works on local and
    return local;
}

Well, this isn't Exactly what happens all of the time, but it boils down to this, more or less. The result will be the same, but compilers may behave differently in this case.

Bottom line is: C returns by value, so your code works fine.
However, there are pitfalls:

struct foo
{
    int member1;
    char *str;
};
struct foo some_f()
{
    char bar[] = "foobar";
    struct foo local = {
        .member1 = 123,
        .str = &bar[0]
    };
    return local;
}

Is dangerous: the pointer assigned to local.str points to memory that will be released once the struct is returned. In that case, the problems you expected with this code are true: that memory is no more (or is not valid anymore).
Simply because a pointer is a variable whose value is the mem address, and that value is returned/assigned.

Akaisteph7
  • 5,034
  • 2
  • 20
  • 43
Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
  • you need to dereference the value for assignment in line struct foo your_var = * some_f(&hidden_stack_space); Yeah i agree with last scenario. – gitesh.tyagi Apr 09 '14 at 09:15
  • @gitesh.tyagi: updated, note that the code in your question contains a `main` function that doesn't return an `int`, and you don't need the parentheses around `items` in `(items).member` =P – Elias Van Ootegem Apr 09 '14 at 09:30
  • code is not mine i copied it and also pasted the reference. updated! – gitesh.tyagi Apr 09 '14 at 10:59
  • @gitesh.tyagi: OK, I see you've also accepted the more complete answer (which I understand, and I'd do the same if I were you). Just a final nit-pick: one could argue that `int main()` would be best written as `int main (void)`. In reality both will work fine, but if a function doesn't take arguments, best explicitly say so in your code. Reasons: self documenting code, confusion about the standard and all that... – Elias Van Ootegem Apr 09 '14 at 11:30
  • Yes you are right, he actually bothered to prove the concept that's why toggled the accepted answer. – gitesh.tyagi Apr 09 '14 at 17:31
6

A struct, at least a large one, will be allocated and returned on the stack, and will be popped off the stack (if at all) by the caller. The compiler will try to allocate it in the same spot where the caller is expecting to find this, but it will make a copy if that is not possible. It is possible, but not necessary that there is also a pointer to the struct, returned via registers.

Of course the details will vary depending on architecture.

Adrian Ratnapala
  • 5,485
  • 2
  • 29
  • 39
6

A common way this is implemented is that the calling routine allocates some space for the structure, typically on the stack, and passes the called routine a pointer to that space. This parameter is not seen in the C source code—it is part of the calling convention of how routines are called in assembly language. Then the called routine simply stores the contents of the structure in the pointed-to space.

In other words, the code is implemented largely as if it had been written:

void foo(struct MyObj *temporary){
    struct MyObj foo_a;
    foo_a.x = 10;
    foo_a.y = 10;
    foo_a.z = 10;
    *temporary = foo_a;
}        

int main () {
    struct MyObj temporary;
    foo(&temporary);
    struct MyObj main_a = temporary;
    return 0;
} 

If the structure is small, it could be returned in a register, or a few registers. Both the called routine and the calling routine would be using the same calling convention, which would have rules about which structures are returned in registers versus which are returned via a pointed provided by the caller.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • sorry still don't get it, `struct MyObj a;` only exist in `foo`'s stack, once foo finishes, it's stack is gone too, so `main_a` is pointing to sth that doesn't exist? –  Oct 22 '20 at 02:34
  • I think that the concept you are having difficulty with is that `main_a` is not a pointer. Think of the statement `return foo_a` in your code as performing a copy operation of all the fields in the structure. It is not assigning a pointer to the structure. – DavidHoadley Oct 22 '20 at 02:41