University of Leeds

Pass-by-value and Pass-by-reference

Introduction

At this point, you should be comfortable with creating functions (and methods) in which we pass arguments to the function and receive a return value. For example, we pass the function radius of a circle and we receive the area of the circle back. This is known as pass-by-value. A simple example is shown below.

main.cpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <iostream>

void increment(int variable);

int main() {
  int variable = 99;
  std::cout << "Variable has a value of: " << variable << std::endl;
  increment(variable);
  std::cout << "Variable now has a value of: " << variable << std::endl;
}

void increment(int variable) {
  variable++;
  std::cout << "This variable has a value of: " << variable << std::endl;
}

The output of the code is shown below.

Variable has a value of: 99
This variable has a value of: 100
Variable now has a value of: 99

We can see we create a integer variable in the main function and give it the value 99. Since the variable is declared in main(), its scope is limited to within main i.e. it is a local variable. When we pass it to the increment function, we are actually passing a copy of the variable, and not the variable itself. Therefore, the integer variable in the increment function is a completely separate variable in a different memory location, so even though we modify its value in the function, it does not affect the value stored in the variable in main.

Pass-by-reference

It is actually very simple to convert this pass-by-value scheme to pass-by-reference. We do it using C++ references as shown below.

main.cpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <iostream>

void increment(int &variable);

int main() {
    int variable = 99;
    std::cout << "Variable has a value of: " << variable << std::endl;
    increment(variable);
    std::cout << "Variable now has a value of: " << variable << std::endl;
}

void increment(int &variable) {
    variable++;
    std::cout << "This variable has a value of: " << variable << std::endl;
}

Note the only difference is the addition of the reference operator & in the argument of the function (and prototype). All the same character & is used as in the address-of operator (valid in both C and C++), when used in this way it is not the same operation and is only valid in C++. When the code is run, we obtain the following output.

Variable has a value of: 99
This variable has a value of: 100
Variable now has a value of: 100

Here we can see that the function has modified the value of the variable local to main(). This is because we passed in an actual reference (or alias) of the variable to the function and not just a copy of it.

Pass-by-reference can also be used to pass in objects to functions and methods. This is where is becomes useful. Consider a C++ class used to represent an LCD object in an embedded systems project. You may have a function to draw a spaceship on the LCD. If you pass in a copy of the LCD object (pass-by-value), when you return back to your calling function, that (original) copy of the LCD object will not have the spaceship on it! You should therefore just have a single LCD object in your project, and pass a reference to it around the different functions. Additionally, the object may be quite large in terms of memory. Therefore, when a copy is created, it will take up extra memory and can slow down operation. By passing around a reference instead, you only need to pass in a memory address to the function, which is much less memory intensive and quicker.

Multiple 'return' values

Another way in which pass-by-reference is useful is when creating functions that need to calculate or modify more than one value. With standard return statements, we can only return a single datatype. If we wish to return more than one, we can actually pass in more than one reference to variables to the function and modify the variables in the function. Since we passed in references, the values in main() will have also changed. This is demonstrated in the example below.

main.cpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <cmath>
#include <iostream>

const double PI = 3.14159265359;

void circular(double radius, double &area, double &volume);

int main() {
  double area, volume;
  circular(1.0, area, volume);
  std::cout << "Area = " << area << ", Volume = " << volume << std::endl;
}

void circular(double radius, double &area, double &volume) {
  area = PI * pow(radius, 2.0);
  volume = (4.0 / 3.0) * PI * pow(radius, 3.0);
}

By passing in variables for the area and volume by reference, the circular function can do the relevant calculations and the results of the calculations will be 'returned' to the main function. The output is below.

Area = 3.14159, Volume = 4.18879