In C, there are two ways to pass a variable as a parameter to a function:
Passing the variable itself. In this case, the function gets its own copy of the variable to work on. Creating a new copy of the variable on the stack can be time-consuming if, for example, the variable is a large structure.
Passing a pointer to the variable. In this case, the function gets only the address of a variable, which it uses to access the caller's copy of the variable. This technique is much faster for large structures.
In C++, you have a third option: passing a reference to the variable. In this case, the function receives an alias to the caller's copy of the variable.
The following example illustrates all three techniques:
// Reference parameters for reducing overhead
// and eliminating pointer notation
#include <iostream.h>
// ---------- A big structure
struct bigone
{
int serno;
char text[1000]; // A lot of chars
} bo = { 123, "This is a BIG structure" };
// -- Three functions that have the structure as a parameter
void valfunc( bigone v1 ); // Call by value
void ptrfunc( const bigone *p1 ); // Call by pointer
void reffunc( const bigone &r1 ); // Call by reference
void main()
{
valfunc( bo ); // Passing the variable itself
ptrfunc( &bo ); // Passing the address of the variable
reffunc( bo ); // Passing a reference to the variable
}
// ---- Pass by value
void valfunc( bigone v1 )
{
cout << '\n' << v1.serno;
cout << '\n' << v1.text;
}
// ---- Pass by pointer
void ptrfunc( const bigone *p1 )
{
cout << '\n' << p1->serno; // Pointer notation
cout << '\n' << p1->text;
}
// ---- Pass by reference
void reffunc( const bigone &r1 )
{
cout << '\n' << r1.serno; // Reference notation
cout << '\n' << r1.text;
}
The parameter r1 is a reference that is initialized with the variable bo when reffunc is called. Inside reffunc, the name r1 is an alias for bo. Unlike the previous examples of references, this reference has a different scope from that of the variable it refers to.
When you pass a reference as a parameter, the compiler actually passes the address of the caller's copy of the variable. Passing a reference is therefore just as efficient as passing a pointer, and, when passing large structures, far more efficient than passing by value. However, the syntax for passing a reference to a variable is identical to that for passing the variable itself. No & is needed in the function call statement, and no -> is needed when using the parameter within the function. Passing a reference thus combines the efficiency of passing a pointer and the syntactical cleanliness of passing by value.
When you pass a reference as a parameter, any modifications to the parameter are actually modifications to the caller's copy of the variable. This is significant because, unlike the syntax of passing a pointer, the syntax of passing a reference doesn't give any indication that such a modification is possible. For example:
valfunc( bo ); // Function can't modify bo
ptrfunc( &bo ); // & implies that function can modify bo
reffunc( bo ); // Same syntax as valfunc;
// implies that function can't modify bo
The syntax for calling reffunc could make you think that the function cannot modify the variable you pass. In the case of reffunc, this assumption is correct. Because reffunc's parameter is a reference to a constant, its parameter is a read-only alias for the caller's copy of the variable. The reffunc function cannot modify the bo variable.
But you can also use an ordinary reference as a parameter instead of a reference to a constant. This allows the function to modify the caller's copy of the parameter, even though the function's calling syntax implies that it can't. For example:
// BAD TECHNIQUE: modifying a parameter through a reference
void print( int &parm )
{
cout << parm;
parm = 0;
};
void main()
{
int a= 5;
print( a ); // Parameter is modified;
// unexpected side effect
}
Using references this way could be very confusing to someone reading your program.
For precisely this reason, you should use caution when passing references as function parameters. Don't assume that a reader of your program can tell whether a function modifies its parameters or not just by looking at the function's name. Without looking at the function's prototype, it is impossible to tell whether a function takes a reference or the variable itself. The function's calling syntax provides no clues.
To prevent such confusion, you should use the following guidelines when writing functions that take parameters too large to pass by value:
If the function modifies the parameter, use a pointer.
If the function doesn't modify the parameter, use a reference to a constant.
These rules are consistent with a common C-programming convention: when you explicitly take the address of a variable in order to pass it to a function, the function can modify the parameter. By following this convention, you make your C++ program more readable to C programmers. This is strictly a coding convention, and cannot be enforced by the compiler. These rules do not make your programs correct or more efficient, but they do make them easier to read and understand.
Note that references are only needed when the parameter to be passed is a large, user-defined type. Parameters of built-in types, such as characters, integers, or floats, can be efficiently passed by value.