References and Functions II (Pass by Reference)

In general, functions are used in programming to perform specific tasks and only return one value (if necessary) after processing any input arguments. It’s important to note that the memory allocated to a function will be released once the function has completed its task and returned any necessary value.

But what if we need more than one value from a particular function? Before discussing this, let’s first become familiar with the concept of reference variables.

Reference variable

To understand reference variables in C++, it’s important to understand the behavior of the ampersand (&) operator first. We can use this & operator to obtain the memory address of a variable.

Let’s look at an example.

Press + to interact
#include <iostream>
using namespace std;
int main()
{
int a = 5;
float c = 2.5;
cout<< "Address of integer variable `a` is: " << &a<<'\n';
cout<< "Address of float variable `c` is: " << &c<<'\n';
return 0;
}

In C++, a reference variable is a way to create an alias for an existing variable. The syntax for creating a reference variable involves using the & operator followed by the name of the variable we want to reference.

For example, consider a variable named a of the type int. To create a reference to a, we can use the following syntax:

int &aRef = a;

Here, aRef is a reference variable that refers to a. Note that the & operator is used to specify that aRef is a reference variable rather than a regular variable. The animation below can help illustrate how a reference variable acts as a new identifier for the same memory location.

Press + to interact
A reference variable
A reference variable
1 of 7

It’s important to note that reference variables share the same memory address as the original variable. This means that any modifications made to the reference variable will also impact the original variable, and vice versa.

To understand this, consider the following code:

Press + to interact
#include <iostream>
using namespace std;
int main()
{
int a = 5;
int &aRef = a;
cout << &a << " --- "<< &aRef <<'\n';
cout << "Before incrementing\n";
cout <<"Value of a : "<< a << " --- Value of aRef "<< aRef<<'\n';
aRef++;
cout <<"Value of a : " <<a << " --- Value of aRef "<< aRef<<'\n';
return 0;
}

Next, let’s look at an example in which we need to return two values.

Swapping values

We’re given two variables—alpha and beta—and we want to swap their values. For that, we shouldn’t do the following:

Press + to interact
#include <iostream>
using namespace std;
int main()
{
int alpha = 5, beta = 4;
alpha = beta; // alpha will become 4 and beta will be 4 too, and previous value of alpha is lost
beta = alpha; // This will be a logical error now as it will assign beta = 4
cout << "alpha: "<< alpha << endl
<< "beta: "<< beta <<endl;
return 0;
}
  • Line 6: We declare two integer variables, alpha and beta, and initializes them with the values 5 and 4, respectively.

  • Line 7: We assign the value of beta to alpha using the assignment operator =. After this line executes, both alpha and beta hold the value 4.

  • Line 8: We assign the value of alpha to beta using the assignment operator =. After this line executes, both alpha and beta hold the value 4.

As a result, the values of alpha and beta have effectively been swapped, but not in the way that we intended. This is because the second assignment overwrites the value of beta with the incorrect value of alpha (which is now equal to the original value of beta).

Fixing the logical error

To fix this error, we need to create the temporary variable temp. The following illustration clearly describes the process of swapping.

Now that we understand the process, let’s write down the code for swapping.

Press + to interact
#include <iostream>
using namespace std;
int main()
{
int alpha = 5;
int beta = 4;
cout<<"Before Swap"<<'\n';
cout << "alpha : " <<alpha<<'\n';
cout << "beta : " <<beta<<'\n';
int temp = alpha;
alpha = beta;
beta = temp;
cout<<"\nAfter Swap"<<'\n';
cout << "alpha : " <<alpha<<'\n';
cout << "beta : " <<beta<<'\n';
return 0;
}
  • Lines 6–7: We define the variables alpha and beta with valid states.

  • Lines 11–13: We swap the values of variable alpha and beta using the temporary variable temp.

Swapping values using functions

When we have to swap the values of two variables multiple times in our code, it can be tedious to repeat the same three lines of code because it can result in repetition. How can we avoid this?

One way to reduce this redundancy is to use a function to perform the swap operation.

Writing swap passed by value (a logical error)

The following code has a function called swap. We call this function erroneous because the parameters are passed by copy, which means that a new copy of each parameter is created in memory when the function is called. When the function returns, any changes made to these copies are lost, and the original variables remain unchanged.

Even if we create variables in the function with the same names as the calling position, the function will still operate on the copies rather than the original variables. This means that any changes made to the variables inside the function will not affect the variables in the calling position, rendering the function erroneous.

Press + to interact
#include <iostream>
using namespace std;
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int alpha = 5;
int beta = 4;
cout<<"Before Swap"<<'\n';
cout << "alpha : " <<alpha<<'\n';
cout << "beta : " <<beta<<'\n';
swap(alpha, beta);
cout<<"After Swap"<<'\n';
cout << "alpha : " <<alpha<<'\n';
cout << "beta : " <<beta<<'\n';
return 0;
}

Writing swap passed by reference (a correct solution)

To solve this problem, we can modify the function signature to include the & operator before the parameter names, which indicates that the parameters are passed by reference. Let’s take a look at the following code to see the correct implementation and how it executes.

Press + to interact
#include <iostream>
using namespace std;
void swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int alpha = 5;
int beta = 4;
cout << "Before Swap" << '\n';
cout << "alpha : " << alpha <<'\n';
cout << "beta : " << beta <<'\n';
swap(alpha, beta);
cout << "After Swap" <<'\n';
cout << "alpha : " << alpha <<'\n';
cout << "beta : " << beta <<'\n';
return 0;
}

This code defines a function called swap that takes two integer parameters by reference. The function swaps the values of the two parameters using a temporary variable.

  • Lines 11–12: We define two integer variables alpha and beta with initial values of 5 and 4, respectively.

  • Lines 13–15: We print the initial values of alpha and beta to the console.

  • Line 16: We call the swap function with alpha and beta as parameters. This will swap the values of alpha and beta in the calling postion as well.

  • Lines 17–19: After the swap, the new values of alpha and beta are printed to the console.

Let’s look at the following animation and see how the correct swap function works.

Press + to interact
A demonstration of memory for swap function
A demonstration of memory for swap function
1 of 12

To gain more practice with passing by reference, we will now solve another nontrivial problem. In particular, we will explore how to simulate the return of multiple values from a function using pass-by-reference arguments. Although returning multiple values directly from a function isn’t possible, we can achieve this functionality by passing arguments by reference.

Exercise: Reduced fraction

You are given a fraction with two values: numerator and denominator. Your task is to reduce the fraction by implementing the functionality of the greatest common divisor (GCD) first and then calculate the reduced values of numerator and denominator.

Remember to compute the reduced fraction; all you have to do is compute the GCD of the numerator and denominator and divide both with the computed GCD.

For example, 1824\frac{18}{24} is not a reduced fraction. But it can become a reduced fraction by computing the GCD(18,24)GCD(18, 24) first, which is 6. Now, divide both the numerator and denominator by 6. The resulting reduced fraction will be (186)(246)=34\frac{(\frac{18}{6})}{(\frac{24}{6})} = \frac{3}{4}.

Remember: You are not required to write the main() function. Just implement the two mentioned functions.

Note: You can't change the name of function and the number of parameters given in the prototypes of the reduceFraction function.

Press + to interact
#include <iostream>
using namespace std;
int GCD(int numerator,int denominator)
{
// Write GCD code here
return 1;
}
void reduceFraction(int &numerator,int &denominator)
{
//Write your code here
}