The difference between pass by reference, pass by value, and pass by pointer.

So after doing a lot of research on this subject I found that there are a lot of issues with what is seen online. Hopefully this article will be able to clear some of the confusion that people are seeing. To get started I would like to first look at passing by reference. How that is different than passing by pointer (which can be hard to see) and then look at how passing by value is different from both pass by reference and pointer.

Passing by reference

We will be working with code, which is below and the language we will be using to illustrate is C++ because it contains a good clear example of all three of these passing types.

#include 
using namespace std;

class Car {
public:
    int id;
};
void SwitchInts (int &num1, int &num2)
{
    int temp = num1;
    num1 = num2;
    num2 = temp;
}
void SwitchCarIds (Car &car1, Car &car2)
{
    Car temp =car1;
    car1 = car2;
    car2 = temp;
}
int main(int argc, const char * argv[])
{
    int first = 1;
    int second = 2;
    cout << "Before\n" << first << "\n" << second << "\n";
    SwitchInts(first, second);
    cout << " After \n" << first << "\n" << second << "\n";

    //Car changes
    Car *car1 = new Car();
    car1->id = 1;
    Car *car2 = new Car();
    car2->id = 2;
    cout << "Before\n" << car1->id << "\n" << car2->id << "\n";
    SwitchCarIds(*car1, *car2);
    cout << " After \n" << car1->id << "\n" << car2->id << "\n";
    return 0;
}

In this code you will find that we have a function for switching two integers and a method for switching two car ids. The method they use is pass by reference, which is indicated by the “&” sign being put in the parameter declarations. So to get started let us look at what the main function does.

The main function will first create two integer handles that are the memory address to a primitive integer value. The program will then print out the value of the two handles called first and second. The order that it will print them is 1, 2. After that, the two handles will be handed into the pass by reference method SwitchInts. This method will take the two objects it was handed and then swap them inside the method. After the method is executed you will find that the order in which the variables are printed is the same. The first variable is printed and then the second. You will find though that the output is different. The output is 2, 1. So what happened here?

Well first and second were assigned 1, 2 respectively and then they were passed by reference. How we know they were passed by reference is that the declaration in the top of the method using int &first indicates that this method will be passed a reference type. This reference type is then used to change the value of two variables. The key word here is that WE ARE PASSED A REFERENCE TO THE MEMORY LOCATION WHERE THE INT VALUE IS. So we are given the literal memory address at which the integer is located so we can directly manipulate that piece of memory, hence after the function call the output is 2, 1.

Now regarding the second half of the main function it does a similar function. It creates two Car objects via pointers *car1, and *car2. Then it sets the id inside of the cars. The first car is given an id of 1 and the second car 2. The program prints them and then passes the cars into the SwitchCarIds method. This method then takes in the two cars and swaps them. If you notice the declaration for the SwitchCarIds method is the same in the fact that it uses the “&” operator. Something different from switching the integers though, is the call to SwitchCarIds passes in two dereferenced pointers. Notice how in the call it passes *car1, *car2 instead of car1, car2. That is because the pointers are different then the int first and second handles. The pointers are just pointers to a memory location. The method being called in this case is a pass by reference method though. Meaning it needs the memory locations which dereferencing the pointer will do. It takes the pointer and passes the memory location into the method instead of the pointer.

Anyways, after that method executes the ids of the cars will be switched. All this to say, passing by reference is where the method is really given a memory location to actually change and manipulate instead of something covering or hiding it from the method.

Passing by pointer

This brings me to my next point which is passing by pointer is where the method is given the things covering the memory address. HOWEVER this does not mean that the pointer makes things unchangeable, it is just something different given to change things. So with that let us look at the next code chunk.

void SwitchInts (int *num1, int *num2)
{
    int temp = *num1;
    *num1 = *num2;
    *num2 = temp;
}
void SwitchCarIds (Car *car1, Car *car2)
{
    Car temp = *car1;
    *car1 = *car2;
    *car2 = temp;
 }


int main(int argc, const char * argv[])
{
    int first = 1;
    int second = 2;
    cout << "Before\n" << first << "\n" << second << "\n";
    SwitchInts(&first, &second);
    cout << " After \n" << first << "\n" << second << "\n";

    //Car changes
    Car *car1 = new Car();
    car1->id = 1;
    Car *car2 = new Car();
    car2->id = 2;
    cout << "Before\n" << car1->id << "\n" << car2->id << "\n";
    SwitchCarIds(car1, car2);
    cout << "After\n" << car1->id << "\n" << car2->id << "\n";
    return 0;
}

Okay so first we need to examine the declarations of the methods. This time they are using “*” instead of the “&” sign. This is basically saying the method is taking in pointer objects instead of reference objects. Just like before both the SwitchInts method and SwitchCarIds method will take in their parameters and switch them. The main method will do the exact same thing as before but instead it will call SwitchInts with using the “&” sign making the call look like SwitchInts(&first, &second);. This is different than before because before there was no use of the “&” sign. What this sign does is take the int reference types it was given and turns them into pointer objects. That is how you take the int reference type and turn it into a pointer type making the call to the SwitchInts pass by pointer method valid.

The next part of the main method does the same thing but it passes in the cars a little differently. It does not change the variables on passing in because the car1 and car2 variables are pointers. Since the SwitchCarIds is pass by pointer there is no need to alter the variables in the call to the method.

This code is not all that different then the code before it, there is just a different passing going on. When ran, this program will do the exact same thing it will print

Before
1
2
After
2
1
Before
1
2
After
2
1

and the variables will be changed. Again, the objects are changed within the methods but it is just a different handle to make the changes. The last way to pass will not allow making these kinds of changes.

Passing by value

This is the easy one. This just takes the value of what was passed into the method and then puts that into the parameter for the method. Below is the code we will use for this.

void SwitchCarIds (Car car1, Car car2)
{
    Car temp = car1;
    car1 = car2;
    car2 = temp;
}

void SwitchInts (int num1, int num2)
{
    int temp = num1;
    num1 = num2;
    num2 = temp;
}

void SpecialSwitchCarIds (Car car1, Car car2)
{
    cout << "The car is the same : " << car1.id << " " << car2.id; }

int main(int argc, const char * argv[])
{
    int first = 1;
    int second = 2;
    cout << "Before\n" << first << "\n" << second << "\n";
    SwitchInts(first, second);
    cout << "After\n" << first << "\n" << second << "\n";

    //Car changes
    Car *car1 = new Car();
    car1->id = 1;
    Car *car2 = new Car();
    car2->id = 2;
    cout << "Before\n" << car1->id << "\n" << car2->id << "\n";
    SwitchCarIds(*car1, *car2);
    cout << "After\n" << car1->id << "\n" << car2->id << "\n";

    SpecialSwitchCarIds(*car1, *car2);
    return 0;
}

This code is a little bit more then the above examples just because another method would be good for explaining.

In this example we are passing by value and if you notice, the declarations for the methods do not use any modifiers they simply say int first, int second and Car car1, Car car2. This is how you pass by value. The extra method SpecialSwitchCarIds will take in the car objects and then print their ids. The main method is exactly the same, except for the call to the SpecialSwitchCarIds method at the bottom and the way the switch methods are called. The SwitchInts method is called simply by saying SwitchInts(first, second) no modifiers. This is straightforward because the first and second are reference types. HOWEVER this is not pass by reference. What happens is it grabs the value of what is located at the reference types address and passes that value into the method. The second call SwitchCarIds again does the dereferencing just like we saw during pass by reference but again what happens is it takes the value of what is at that memory location and passes it into the method, not the memory location so you can not actually change the value of the variables car1 and car2 in the main method.

The output of this program is :
Before
1
2
After
1
2
Before
1
2
After
1
2

The car is the same : 1 2

So the main difference with pass by value is that it does not give any major handles to the methods. The methods are merely given a “copy” of the value located at the reference types address. Hence, protecting the outside variables from being changed. Where this can be seen is in the SpecialSwitchCarIds method, when this is ran it will print the same ids but they are not the exact same memory location, which is why if you changed the SwitchSpecialCarIds function to look like this

void SpecialSwitchCarIds (Car car1, Car car2)
{
    int temp = car1.stuff;
    car1.stuff = car2.stuff;
    car2.stuff = temp;
}
And the call like this
cout << "Before\n" << car1->stuff << "\n" << car2->stuff << "\n";
SpecialSwitchCarIds(*car1, *car2);
cout << "After\n" << car1->stuff << "\n" << car2->stuff << "\n";

The variables would not be changed in the main function.

So the next question is how this applies to Java and C #like languages. Well both of these languages are pass by value languages and C# actually allows the other type, pass by reference. They act exactly the same in C++ as they do in both languages. Where you see online the confusion though is that people will say that C# and Java are indeed pass by reference languages, however this is untrue. What really is happening is that C# and Java are passing the value of the reference and that is it. In C# you can however use the ref keyword to denote you would like to pass by reference. The following C# program is a good demonstration of this.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RefValPionter
{
    class Program
    {
        class Car
        {
            public int something;
            public void ChangeCar(Car theCar)
            {
                theCar = new Car();
                theCar.something = 2;
            }
        }
        static void Main(string[] args)
        {
            Car temp = new Car();
            temp.something = 1;
            temp.ChangeCar(temp);
            Console.WriteLine(temp.something);
            Console.Read();
        }

    }
}

This concludes my adventures in researching the difference between passing by reference, passing by pointer, and passing by value.