There are basically two cases. (1) The return value is something other than a struct, and (2) the return value is a struct.
For case (1), the return value is typically in a register -- it used to always be r0, or maybe f0 in the case of floating-point returns, or maybe r0+r1 in the case of long int
returns.
In that case, when you have something like
int a()
{
return 3;
}
int b()
{
return a();
}
the compiler basically compiles b()
to some code that calls function a
, and that's it. Function a
returns its value in whichever register int
-valued functions return, and that's just where it needs to be for function b
to return it, so there's nothing else to do; the value is already where it belongs. And therefore, at least in this case, there's no extra copying involved.
(This can also lead to situations where seemingly "wrong" code works anyway, and thus this Frequently-Asked Question: Function returns value without return statement?.)
But then, in your function main
where you did int B = b()
, then yes, there may be a "copy" from the return register to B
's location. (Although, these days, a smart compiler may remember that "for now, r0
is B
".)
For structs, on the other hand (that is, case 2), and especially for large ones, the compiler typically passes an extra, hidden argument which is a pointer to the location in the caller where the return value should go. That is, if you have
struct largestruct bb();
int main()
{
struct largestruct B;
B = bb();
}
it will be compiled more or less as if you had written
void bb(struct largestruct *);
int main()
{
struct largestruct B;
bb(&B);
}
So if you then have
extern struct largestruct aa();
struct largestruct bb()
{
return aa();
}
It will probably be compiled as if it were written
extern void aa(struct largestruct *);
void bb(struct largestruct *__retp)
{
aa(__retp);
}
And, again, it's more or less true that "the pointer points to the right place, and no copy is needed".