Before studying "parameter passing by reference", a student should develop a clear understanding of the simpler technique of "parameter passing by value". So we start with this concept below.
The C++ Language allows any function to return a single value through its function name, provided that the identifier was declared with a data type appropriate to the value being returned. For example, if we wanted a function that would acquire a person's age from the keyboard and return it to the parent process, we might write the source code as follows:
int ReadAge () /* Function Header */
{
int A; /* Define (allocate) a local variable to temporarily store an age */
cout << "Enter your age: "; /* Prompt for Age */
cin >> A; /* Read keyboard input buffer for a decimal integer */
return A; /* Send A back to the parent via the function label */
} /* Local variable A is abandoned here */
Based on the function header, the function identifier ReadAge is declared as an integer,
meaning that it can store a whole number (to be used by its parent function - main).
Notice that the ReadAge function has an empty formal parameter list, meaning that the
function receives no data from its parent when called.
The return statement causes the contents of local variable A to be assigned into the
function label ReadAge which is typically a global identifier known by main
(and other functions).
The function above could be called by any program that included it and had an integer
variable declared to receive the returned value. If we wrote a program with an integer
variable named AGE, we could call the function above with the statement:
AGE = ReadAge ();
The statement above calls the function ReadAge to request and return input that was
read from the keyboard. The calling function (main) does not pass any data into this
function when it starts, so the "actual parameter list" (following the
function label in parentheses) is empty. Because the function ReadAge returns a
value, it is handled like a value and assigned into a variable.
When a parameter is passed from one function to another by copying the value itself into an identifier known by the receiving function, we refer to that passage as "passing by value". This is the easiest way to pass a parameter. But it is not always possible. For example, when we want a function to return more than one value to it's parent, the method described above is not suitable. A function has only one label, so we cannot use that label to return more than one value. Another method for parameter passing is needed that does not require the use of the function name. For this situation we pass parameters by reference. This technique uses either reference variables or pointers to pass information about the addresses of the intended receiving variables into a function, which then uses that information (rather than the funcutions's single identifier) to return as many parameters as need to the parent function.
The rest of this document illustrates three different techniques for passing parameters into a child function and then returning a value back from that function to the parent function that called it. The child function in each complete program code example accomplishes the same objective: to receive a floating point number, passed-in from the main function, and then to divide the number by 4 and return the result to the calling function (main), replacing the original number with one quarter its original value. The name of the function ("quarter") will be the same in each sample program, but the techniques used for the passing of the parameters will be quite different. In each of the code examples below, pay attention to both: the statements defining the function quarter, and the statement in the main function that calls (executes) the quarter function.
// param_pass_value.cpp
// Demonstrating: "passage by value"
#include <iostream>
using namespace std;
// CHILD FUNCTION (Must be declared ahead of its calling statement)
float Quarter (float N)
/* Formal parameter N receives a float value from main's variable Num because of
its use in main as the actual parameter in the calling statement for Quarter */
{
float R; // Local variable to hold the result of a calculation
R = N/4; // Divide N by 4 and assign result to local variable R
return R; // Terminate function and pass R into identifier Quarter
/* Note: the three statements above could be written more simply as just:
return N/4;
elminating the need to allocate and use a local variable, R.
The function identifier Quarter is involved in the return of the value,
so it is declared as data type "float". */
}
// PARENT FUNCTION
int main()
{
float Num; // A number entered by the user and quartered
cout << "Enter a number to be quartered: ";
cin >> Num;
Num = Quarter (Num); // Send Num and assigned returned value back into it
cout << "One quarter of that value is " << Num << endl;
return 0; // Terminate function and pass 0 into identifier main
}
A reference variable is an identifier being used as an alias for another variable (typically outside of the current function's scope). The use of a reference variable as a formal parameter allows any change to it to alter the value in its related actual parameter, unlike normal parameters which normally receive copies of the data from actual parameters, but do not allow changes to be sent back to them. The passage of data into a function via a reference variable is referred to as passage by reference. A reference variable is declared in the formal parameter list of a function by placing the symbol & (ampersand) immediately after the data type indicator (as in: int &). The blank space is optional. The data type indicated for the reference variable must match the data type of its related actual parameter,
which must be a variable, not any type of constant.
For example, the declaration for a reference variable named N in the code example below as an alias for an actual parameter of type float is:
float &NIn the declaration above, the identifier N acts as a "reference to a float". Although the ampersand (
&) touches the identifier, it is not part of it. Note that the identifier is just N, not &N.
// param_pass_refvar.cpp
// Demonstrating: "passage by reference" using a "reference variable"
#include <iostream>
using namespace std;
// CHILD FUNCTION (Must be declared ahead of its calling statement)
void Quarter (float &N)
/* Formal parameter N is declared as a "reference variable" as indicated by the
ampersand (&) following the data type indicator (float in this example).
It relates to main's variable Num because of its use in the main function
as the actual parameter in the calling statement for Quarter */
{
N = N/4; // Divide N (which is Num) by 4 and reassign it back to N (Num)
return; // Empty return optional, but not used for data passage
/* Because N is an alias for Num (the actual argument in the parent,
any change to N is also a change to Num.
The function identifier Quarter is not involved in the return of the value,
so it is declared as data type "void". */
}
// PARENT FUNCTION
int main()
{
float Num; // A number entered by the user and quartered
cout << "Enter a number to be quartered: ";
cin >> Num;
Quarter (Num); // Send Num which is altered by Quarter
cout << "One quarter of that value is " << Num << endl;
return 0; // Terminate function and pass 0 into identifier main
}
// param_pass_refptr.cpp
// Demonstrating: "passage by reference" using a "pointer variable"
#include <iostream>
using namespace std;
// CHILD FUNCTION (Must be declared ahead of its calling statement)
void Quarter (float *N)
/* Formal parameter N is declared as a "pointer". N receives the address of main's
variable Num because it was accessed using the unary & ("address of") operator
in main as the actual parameter in the calling statement for Quarter */
{
*N = *N/4; // Dereference N (which points at Num) to alter its value.
return; // Empty return optional, but not used for data passage
/* N is a pointer that contains the address of Num (the actual argument in the
parent function (main). The prefix use of the unary dereferencing operator *
allows us to refer to the actual argument Num without using its identifier
Num (which is not within the scope of the function Quarter).
The function identifier Quarter is not involved in the return of the value,
so it is declared as data type "void". */
}
// PARENT FUNCTION
int main()
{
float Num; // A number entered by the user and quartered
cout << "Enter a number to be quartered: ";
cin >> Num;
Quarter (&Num); // Send the ADDRESS OF Num (altered thru indirection).
cout << "One quarter of that value is " << Num << endl;
return 0; // Terminate function and pass 0 into identifier main
}
Consider the following situation that would involve passing more than one (two in this case) parameters from a child function back to its parent. If we wanted a function that would acquire both a person's height in feet (floating point data) and weight in whole pounds from the keyboard and return both of them to the parent process, we could write the source code as follows:
void ReadHandW (float *HPTR, int *WPTR) // Function Header
{
float H; // Height in feet
int W; // Weight in pounds
cout << "Enter your height in feet (decimals OK): ";
cin >> H; // Read keyboard input buffer for a float
cout << "Enter your weight in pounds (whole number): ";
cin >> W; // Read keyboard input buffer for a float
*HPTR = H; // Return H by using a pointer to a variable in the parent
*WPTR = W; // Return W by using a pointer to a variable in the parent
} // All local variables and parameters are abandoned here
Based on the function header, the function name ReadHandW is declared as void,
meaning that it can store no data. However, the ReadHandW function has a formal parameter list
that is defined to receive pointers (references to addresses of variables in the parent).
The last two statements at the end of the function use indirection
(assignment using the pointers) to return the two parameters to the parent function.
The function label is not involved in returning the parameters and no return statement is used.
This function could be called by any program that included it and had both float and integer variables declared to receive the returned values. If we wrote a program with a float variable named HEIGHT and an integer variable named WEIGHT, we could call the function above with the statement:
ReadHandW (&HEIGHT, &WEIGHT); // Call a function, passing pointers to intended target variables
The statement above calls the function ReadHandW to acquire and return two items of input
to be read from the keyboard. The calling function (main) passes the addresses of the target variables
into this function when it starts. Notice the use of the ampersand operators on the actual parameter
labels. The function ReadHandW returns its parameters indirectly
to the variables using the addresses it was passed. Because it does not use its name
to hold any of the values being returned, there is no assignment shown in the calling statement.