1. Introduction
Variables play an important role in computer programming because they enable programmers to write flexible programs. A very important aspect that we need to keep in mind with variables is when we pass variables them as arguments to a function.
This tutorial explores what happens under the hood when the caller of a function passes arguments to the callee by value and by reference.
2. Call by Value
Call by value, also known as pass by value, is the most common way of passing arguments. When we pass a parameter by value, the caller and callee have two independent variables with the same value. Therefore, if the callee modifies its variable, the effect is not visible to the caller. Indeed, it has its own copy of the variable:
function callee(x passed by value):
x <- x + 1
function caller():
a <- 5
print a
callee(value of a)
print a
// Output when passing the parameter by value:
// 5
// 5
In the example above, the caller creates a variable and assigns the value to it. At this point, when we print the value of we clearly get as a result.
The crucial step is when the caller calls the callee, passing the variable by value. The operating system creates a new independent variable with the same value as . When the callee modifies the variable by simply adding to it, the effects cannot be seen by the caller. Indeed, when the caller prints the variable for the second time, we still get as a result.
3. Call by Reference
When we pass a variable by reference, the parameter inside the callee refers to the same object that the caller passed. As a consequence, any change operated by the callee on the object will be seen by the caller as well.
In other words, when a parameter is passed by reference, the caller and the callee use the same variable. If the function being called modifies this variable, the effect is visible to the caller’s variable:
function callee(x passed by reference):
x <- x + 1
function caller():
a <- 5
print a
callee(reference to a)
print a
// Output when passing the parameter by reference:
// 5
// 6
As in the previous example, the caller creates a variable and assigns the value to it. When we print it, we get as a result.
Then, the caller calls the callee, passing the variable by reference. The operating system creates an implicit reference to variable , rather than a brand new variable containing a copy of ‘s value. When the callee adds to , the effects can be seen by the caller. Indeed, the subsequent print statement returns as a result.
4. Call by Value and Call by Reference in Modern Languages
Modern programming languages usually store data on the heap. Only “pointers” to it are ever held in variables and passed as parameters.
Passing such a pointer is still pass by value because a variable’s value is technically the pointer itself, not the pointed object. However, the final effect on the program can be the same as either pass by value or pass by reference:
- If the caller passes a pointer to the callee, this has the same effect as pass by reference. Indeed, the caller will see the changes to the referred object. However, if the callee reassigns the variable holding this pointer then the variable will stop pointing to that object. Any further operations on this variable will instead affect whatever it is pointing to now.
- If the caller passes a deep copy of an object to the callee then we can have the same effect as pass by value. Moreover, some programming languages have “immutable” types which always have the effect of the call by value when we pass them as arguments.
By using call by reference, we have access to an additional channel of communication between the called function and the calling function. However, passing a variable by reference makes it more difficult to track the effects of a function call, and may introduce subtle bugs.
5. Conclusion
In this article, we’ve introduced the concepts of pass by value and pass by reference.