Pass by Argument + Return by Value= double copy?


Hello, I am a beginner, and I am not too sure if the following statements are correct or not.

Since passing an argument by value creates a copy of it, and return a variable by value also creates a copy of it. Thus when we use these two for one function, are we actually creating two copies? One is the copy of the argument (pass by value), and another one is the copy of that copy of the parameter (return by value)?

Here is an example:

#include <iostream>
using namespace std;

int ReturnAVariable(int Input)
{
return Input;
}

int main()
{
int MyVariable=10;
ReturnAVariable(MyVariable);

return 0;
}

So when we pass 'MyVariable' to the function ReturnAVariable, we are creating a copy of MyVariable. What happens when we try to return this copy of MyVariable by value? Do we just create a copy of that copy of MyVariable?

I understand that these codes are not useful and my question sounds very stupid, but I just want to know whether my thought is correct or not. Can anyone help me with this please?
Yes, we make copies, but often the compiler can detect the inefficiency and optimize it.

Have you learned about references and reference parameters? One reason for using them is to avoid exactly this sort of unnecessary copying. It doesn't make much difference when you're passing something small like an integer, but if the parameter is a large piece of data then it can make a big difference.
Thanks for your reply, and yes I have learnt about the passing by reference. So it's true that in this particular case there will be two copies? One is the copy of the parameter, and the other one is the copy of the copy of the parameter?
Another variable is not created when you return, but another memory copy is made from the variable being returned (local variable Input) to the original variable (MyVariable).

As a general rule, for in parameters, you should pass built-in types (int, double, ...) by value and everything else as a const reference. Non-const references should only be used for out or in/out parameters.

As dhayden said, the compiler can often optimise your code. In fact, modern compilers are really quite good at it, and the cases where you need to use by-reference rather than by-value have changed. But the general rule I just mentioned is a good starting point.

In your example, nothing is actually left after it's optimised;

For Visual C++ 2013 compiling:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;

int ReturnAVariable(int Input)
{
	return Input;
}

int main()
{
	int MyVar = 10;

	int RetVar = ReturnAVariable(MyVar);

	cout << RetVar;

	return 0;
}


all that's left, in the optimised build, is;

--- w:\source\explore\cplusplus_vc12\temp_test\main.cpp ------------------------
	int MyVar = 10;

	int RetVar = ReturnAVariable(MyVar);

	cout << RetVar;
011512A0  mov         ecx,dword ptr ds:[1153030h]  
011512A6  push        0Ah  
011512A8  call        dword ptr ds:[1153038h]  

	return 0;
011512AE  xor         eax,eax  
}
011512B0  ret


Basically, it's loading the literal value (0Ah is hex for 10) on to the stack and then calling cout to display it.

Andy
Last edited on
For comparison, the unoptimised code (where you see the memory being copied about; the mov operations) is:

int main()
{
00832C80  push        ebp  
00832C81  mov         ebp,esp  
00832C83  sub         esp,0D8h  
00832C89  push        ebx  
00832C8A  push        esi  
00832C8B  push        edi  
00832C8C  lea         edi,[ebp-0D8h]  
00832C92  mov         ecx,36h  
00832C97  mov         eax,0CCCCCCCCh  
00832C9C  rep stos    dword ptr es:[edi]  
	int MyVar = 10;
00832C9E  mov         dword ptr [MyVar],0Ah  

	int RetVar = ReturnAVariable(MyVar);
00832CA5  mov         eax,dword ptr [MyVar]  
00832CA8  push        eax  
00832CA9  call        ReturnAVariable (08314A1h)  
00832CAE  add         esp,4  
00832CB1  mov         dword ptr [RetVar],eax  

	cout << RetVar;
00832CB4  mov         esi,esp  
00832CB6  mov         eax,dword ptr [RetVar]  
00832CB9  push        eax  
00832CBA  mov         ecx,dword ptr ds:[8400F0h]  
00832CC0  call        dword ptr ds:[8400FCh]  
00832CC6  cmp         esi,esp  
00832CC8  call        __RTC_CheckEsp (0831325h)  

	return 0;
00832CCD  xor         eax,eax  
}
00832CCF  pop         edi  
00832CD0  pop         esi  
00832CD1  pop         ebx  
00832CD2  add         esp,0D8h  
00832CD8  cmp         ebp,esp  
00832CDA  call        __RTC_CheckEsp (0831325h)  
00832CDF  mov         esp,ebp  
00832CE1  pop         ebp  
00832CE2  ret 

and

int ReturnAVariable(int Input)
{
00833F30  push        ebp  
00833F31  mov         ebp,esp  
00833F33  sub         esp,0C0h  
00833F39  push        ebx  
00833F3A  push        esi  
00833F3B  push        edi  
00833F3C  lea         edi,[ebp-0C0h]  
00833F42  mov         ecx,30h  
00833F47  mov         eax,0CCCCCCCCh  
00833F4C  rep stos    dword ptr es:[edi]  
	return Input;
00833F4E  mov         eax,dword ptr [Input]  
}
00833F51  pop         edi  
00833F52  pop         esi  
00833F53  pop         ebx  
00833F54  mov         esp,ebp  
00833F56  pop         ebp  
00833F57  ret 
Last edited on
Thanks for your detail explanation, Andy. Now I understand what's going on! This community is great!
Topic archived. No new replies allowed.