11 July 2012

Passing arguments by value, by pointer and by reference

Hi again, today I am going to "speak" about very very familiar topic - argument passing. Almost every tech interview has a question like why do we pass an argument by reference, or what to do when passing an object as a parameter to avoid copying, and so on. I want to tell a little on how the argument passing is handled and what everyone should know.

For our examples let's write a small class with one member of type int and two methods for retrieving that member and also for retrieving the object address. Here we go:


class my_type
{
       int value;

public:

       my_type() : value(11)
       {
       }

       int get_value() const
       {
              return value;
       }

       const void * get_address() const
       {
              return this;
       }
};


Let's start from the very easy case: passing arguments by value.
To pass an argument by value we need to specify the argument type in the function declaration as follows:


void by_value(my_type arg)
{
       std::cout << "Value: " << arg.get_value() << std::endl;
       std::cout << "Address: " << arg.get_address() << std::endl;
}


Now let's try it.


int main()
{
       my_type m;
       std::cout << "Original Value: "
                       << m.get_value() << std::endl;
       std::cout << "Original Address: "
                       << m.get_address() << std::endl;
       by_value(m);

       return 0;
}

I compiled and executed this code and got the following output:
Original Value: 11
Original Address: 0043FB38
Value: 11
Address: 0043FA64
Press any key to continue . . .


As you see the addresses of original object and the argument differ, but the values are the same. So most probably the object has been copied when passing as an argument, more precisely the function argument holds a copy of the original passed object. We can go further and make sure that the object is really copied just by declaring a copy constructor in the private section of the class my_type. If you compile then, the compiler will complain on the by_value function call line, as it is trying to copy the object.

So our conclusion is that passing argument by value always copies the original object.
Now let's go to the next case: passing arguments by pointer.


To pass an argument by pointer the function signature should have a 'pointer to the type of object' as a parameter type, i.e.

void by_pointer(my_type *pointer)
{
       std::cout << "Value: " << pointer->get_value() << std::endl;
       std::cout << "Address: " << pointer->get_address() << std::endl;
}



Now we are going to call this function.

int main()
{
       my_type m;
       std::cout << "Original Value: "
                       << m.get_value() << std::endl;
       std::cout << "Original Address: "
                       << m.get_address() << std::endl;
       by_pointer(&m);

       return 0;
}

The output of the execution is as follows:
Original Value: 11
Original Address: 003DFD3C
Value: 11
Address: 003DFD3C
Press any key to continue . . .


So now the value is the same, and the address is the same either, thus, we can state that the object has not been copied this time. To make sure we again add a private copy constructor to the class body and rerun. No problem this time, as the function does not need to copy the passed in object. Good for now. So what happened really? Let's modify the code a little to see the details. What I will do now is just add a line of code in by_pointer function body to print out also the address of the pointer itself:

std::cout << "Pointer address: " << &pointer << std::endl;

Also we need to update the code in main to see the address of the original pointer, but as we cannot take the address of the rvalue we should create a pointer explicitly. So the modified main function body follows:

my_type *mp = new my_type;
std::cout << "Original Value: "
   << mp->get_value() << std::endl;
std::cout << "Original Address: "
   << mp->get_address() << std::endl;
std::cout << "Address of original pointer: "
   << &mp << std::endl;
by_pointer(mp);

return 0;

Now the output of the execution will be the following:
Original Value: 11
Original Address: 002BD9F0
Address of original pointer: 0024FD70
Value: 11
Address: 002BD9F0
Pointer address: 0024FC7C
Press any key to continue . . .


As you see the object is not copied, but the pointer to the passed object is. So if we consider the pointer itself the passed argument we can again state, that the passed argument is copied. The object is not what we passed as an argument therefore it is not copied.

The last case is passing argument by reference. As the compiler treats the reference as an alias for the referring object, so we cannot write a code to check whether the reference differs from the object. Maybe one can check it in the assembly code, but I am not good at it, so I will just bring up the idea of how this case is handled.

To pass an argument by reference you need to specify the type as reference to the type of argument using the ampersand ('&') symbol:

void by_ref(my_type& ref)
{
       // do something
}


The call of the function will look exactly the same as in the call-by-value case. If we again modify main function and execute, we will see that the object again is not copied (again make a private copy constructor). Any function should know its arguments' locations in memory to be able to access them, so it means that passing by reference is almost the same as passing by address. The reference is treated as an alias but actually it holds the address of an object. So in this case also, the reference is copied to the function arguments, to learn the undergoing details, look through the assembly code or search more on this topic on the internet, but the idea is given here.

Thus, the conclusion is: the passed argument is copied, no matter whether the argument is an object, a reference or a pointer. Note, that I don't say "whether it is passed by value, by pointer or by reference". I hope some people will find something new for them in this post, and the others will just revise what they already know. Thank you for visiting my blog and your time spent reading this article.

1 comment: