Parameter-Passing Modes


Contents


Overview

In Java, all parameters are passed by value. In C++, a parameter can be passed by:

  1. value,
  2. reference, or
  3. const-reference

Each parameter's mode is determined by the way it is specified in the function's header (the mode is the same for all calls to the function). For example:

void f( int a, int &b, const int &c );

Parameter a is a value parameter, b is a reference parameter, and c is a const-reference parameter.

Value Parameters

When a parameter is passed by value, a copy of the parameter is made. Therefore, changes made to the formal parameter by the called function have no effect on the corresponding actual parameter. For example:

void f(int n) {
    n++;
}
 
int main() {
    int x = 2;
    f(x);
    cout << x;  
}

In this example, f's parameter is passed by value. Therefore, although f increments its formal parameter n, that has no effect on the actual parameter x. The value output by the program is 2 (not 3).

Note that if a pointer is passed by value, then although the pointer itself is not affected by changes made to the corresponding formal parameter, the object pointed by the pointed can be changed. For example:

void f(int *p) {
    *p = 5;
    p = NULL;
}
 
int main() {
    int x=2;
    int *q = &x;
    
    f(q);
 
    // here, x == 5, but q != NULL
}

In this example, f's parameter is passed by value. Therefore, the assignment p = NULL; in f has no effect on variable q in main (since f was passed a copy of q, not q itself). However, the assignment *p = 5: in f, does change the value pointed to by q. To understand why, consider what happens when the example program runs:

After executing the two statements:
    int x=2;
    int *q = &x;
 
memory looks like this:
 
       +---+
    x: | 2 | <--+
       +---+    |
                |
       +---+    |
    q: | --|----+
       +---+
 
Now function f is called; the value of q (which is the address of x)
is copied into a new location named p:
 
 
       +---+
    x: | 2 | <--+  <--+
       +---+    |     |
                |     |
       +---+    |     |
    q: | --|----+     |
       +---+          |
                      |
       +---+          |
    p: | --|----------+
       +---+
 
Executing the two statements in f:
    *p = 5;
    p = NULL;
 
causes the values of x (the thing pointed to by p) and p to be changed:
 
       +---+
    x: | 5 | <--+
       +---+    |
                |
       +---+    |
    q: | --|----+
       +---+     
                 
       +----+     
    p: |NULL|
       +----+
 
However, note that q is NOT affected.

Reference Parameters

When a parameter is passed by reference, conceptually, the actual parameter itself is passed (and just given a new name -- the name of the corresponding formal parameter). Therefore, any changes made to the formal parameter do affect the actual parameter. For example:

void f(int &n) {
    n++;
}
 
int main() {
    int x = 2;
    f(x);
    cout << x;  
}

In this example, f's parameter is passed by reference. Therefore, the assignment to n in f is actually changing variable x, so the output of this program is 3.

When you write a function whose purpose is to compute two or more values, it makes sense to use reference parameters (since a function can return only one result). For example, if you want to read a list of integers from a file, and you want to know both how many integers were read, as well as the average value that was read, you might use a function like the following:

void f(istream &input, int &numRead, double &average) {
    int k, sum = 0;
    numRead = 0;
 
    while (intput >> k) {
        numRead++;
  sum += k;
    }
    average = (double)sum/numRead;
}

Another common use of reference parameters is for a function that swaps two values:

void swap( int &j, int &k ) {
    int tmp = j;
    j = k;
    j = tmp;
}

This is useful, for example, in sorting an array, when it is often necessary to swap two array elements. The following code swaps the jth and kth elements of array A:

swap(A[j], A[k]);

Const-Reference Parameters

Another reason to use reference parameters is when you don't want the function to modify an actual parameter, but the actual parameter is very large, and you want to avoid the overhead of creating a copy. Of course, this only works if the function does not modify its formal parameter. To be sure that the actual parameter is not "accidentally" modified, you should use a const-reference parameter. Declaring the parameter to be const tells the compiler that it should not be changed; if the function does change the parameter, you will get a compile-time warning (possibly an error on some systems). For example:

void f(const IntList &L) {
 -- the code here cannot modify L or the compiler will complain --
}

The potential use of a const-reference parameter is the reason why member functions that do not modify any data members should be declared const. For example, suppose that the IntList Print member function was not declared const. Then the following code would cause a compile-time error:

void f(const IntList &L) {
  L.Print(cout);
}

Because L is a const-reference parameter, it is the compiler's job to be sure that L is not modified by f (and that means that no data members of L are modified). The compiler doesn't know how the Print function is implemented; it only knows how it was declared, so if it is not declared const, it assumes the worst, and complains that function f modifies its const-reference parameter L.

Array Parameters

Another unfortunate thing about C++ arrays is that they are always passed by reference (even though you don't declare them to be reference parameters). For example:

void f(int A[]) {
    A[0] = 5;
}
 
int main() {
    int B[10];
    B[0] = 2;
    f(B);
    cout << B[0] << endl;  // the output is 5
}

Although f's parameter looks like it is passed by value (there is no &), since it is an array it is actually passed by reference, so the assignment to A[0] is really assigning to B[0], and the program prints 5 (not 2).

If you want to pass an array by value, you should use a vector, not a regular C++ array (see the last section in the notes on C++ classes for information about vectors).