C++

Null Pointers and Null References

When pointers are not initialized, or when they are deleted, they ought to be assigned to null (0). This is not true for references. In fact, a reference cannot be null, and a program with a reference to a null object is considered an invalid program. When a program is invalid, just about anything can happen. It can appear to work, or it can erase all the files on your disk. Both are possible outcomes of an invalid program.

Most compilers will support a null object without much complaint, crashing only if you try to use the object in some way. Taking advantage of this, however, is still not a good idea. When you move your program to another machine or compiler, mysterious bugs may develop if you have null objects.

Passing Function Arguments by Reference

In Unit 5, "Functions," you learned that functions have two limitations: Arguments are passed by value, and the return statement can return only one value.

Passing values to a function by reference can overcome both of these limitations. In C++, passing by reference is accomplished in two ways: using pointers and using references. The syntax is different, but the net effect is the same. Rather than a copy being created within the scope of the function, the actual original object is passed into the function.

Passing an object by reference allows the function to change the object being referred to.

Recall that Listing 5.5 in Unit 5 demonstrated that a call to the swap() function did not affect the values in the calling function. Listing 5.5 is reproduced here as Listing 9.5, for your convenience.

Listing 9.5. Demonstrating passing by value.

1: //Listing 9.5 Demonstrates passing by value

2:

3: #include <iostream.h>

4:

5: void swap(int x, int y);

6:

7: int main()

8: {

9: int x = 5, y = 10;

10:

11: cout << "Main. Before swap, x: " << x << " y: " << y << "\n";

12: swap(x,y);

13: cout << "Main. After swap, x: " << x << " y: " << y << "\n";

14: return 0;

15: }

16:

17: void swap (int x, int y)

18: {

19: int temp;

20:

21: cout << "Swap. Before swap, x: " << x << " y: " << y << "\n";

22:

23: temp = x;

24: x = y;

25: y = temp;

26:

27: cout << "Swap. After swap, x: " << x << " y: " << y << "\n";

28:

29: }

OUTPUT 

Main. Before swap, x: 5 y: 10

Swap. Before swap, x: 5 y: 10

Swap. After swap, x: 10 y: 5

Main. After swap, x: 5 y: 10

ANALYSIS This program initializes two variables in main() and then passes them to the swap() function, which appears to swap them. When they are examined again in main(), they are unchanged! The problem here is that x and y are being passed to swap() by value. That is, local copies were made in the function. What you want is to pass x and y by reference.

There are two ways to solve this problem in C++: You can make the parameters of swap() pointers to the original values, or you can pass in references to the original values.

Making swap() Work with Pointers

When you pass in a pointer, you pass in the address of the object, and thus the function can manipulate the value at that address. To make swap() change the actual values using pointers, the function, swap(), should be declared to accept two int pointers. Then, by dereferencing the pointers, the values of x and y will, in fact, be swapped. Listing 9.6 demonstrates this idea.

Listing 9.6. Passing by reference using pointers.

1: //Listing 9.6 Demonstrates passing by reference

2:

3: #include <iostream.h>

4:

5: void swap(int *x, int *y);

6:

7: int main()

8: {

9: int x = 5, y = 10;

10:

11: cout << "Main. Before swap, x: " << x << " y: " << y << "\n";

12: swap(&x,&y);

13: cout << "Main. After swap, x: " << x << " y: " << y << "\n";

14: return 0;

15: }

16

17: void swap (int *px, int *py)

18: {

19: int temp;

20:

21: cout << "Swap. Before swap, *px: " << *px << " *py: " << *py << "\n";

22:

23: temp = *px;

24: *px = *py;

25: *py = temp;

26:

27: cout << "Swap. After swap, *px: " << *px << " *py: " << *py << "\n";

28:

29: }

OUTPUT

Main. Before swap, x: 5 y: 10

Swap. Before swap, *px: 5 *py: 10

Swap. After swap, *px: 10 *py: 5

Main. After swap, x: 10 y: 5

ANALYSIS Success! On line 5, the prototype of swap() is changed to indicate that its two parameters will be pointers to int rather than int variables. When swap() is called on line 12, the addresses of x and y are passed as the arguments. On line 19, a local variable, temp, is declared in the swap() function. Temp need not be a pointer; it will just hold the value of *px (that is, the value of x in the calling function) for the life of the function. After the function returns, temp will no longer be needed.

On line 23, temp is assigned the value at px. On line 24, the value at px is assigned to the value at py. On line 25, the value stashed in temp (that is, the original value at px) is put into py.

The net effect of this is that the values in the calling function, whose address was passed to swap(), are, in fact, swapped.

Implementing swap() with References

The preceding program works, but the syntax of the swap() function is cumbersome in two ways. First, the repeated need to dereference the pointers within the swap() function makes it error-prone and hard to read. Second, the need to pass the address of the variables in the calling function makes the inner workings of swap() overly apparent to its users.

It is a goal of C++ to prevent the user of a function from worrying about how it works. Passing by pointers takes the burden off of the called function, and puts it where it belongs—on the calling function. Listing 9.7 rewrites the swap() function, using references.

Listing 9.7. swap() rewritten with references.

1: //Listing 9.7 Demonstrates passing by reference

2: // using references!

3:

4: #include <iostream.h>

5:

6: void swap(int &x, int &y);

7:

8: int main()

9: {

10: int x = 5, y = 10;

11:

12: cout << "Main. Before swap, x: " << x << " y: " << y << "\n";

13: swap(x,y);

14: cout << "Main. After swap, x: " << x << " y: " << y << "\n";

15: return 0;

16: }

17:

18: void swap (int &rx, int &ry)

19: {

20: int temp;

21:

22: cout << "Swap. Before swap, rx: " << rx << " ry: " << ry << "\n";

23:

24: temp = rx;

25: rx = ry;

26: ry = temp;

27:

28: cout << "Swap. After swap, rx: " << rx << " ry: " << ry << "\n";

29:

30: }

OUTPUT

Main. Before swap, x:5 y: 10

Swap. Before swap, rx:5 ry:10

Swap. After swap, rx:10 ry:5

Main. After swap, x:10, y:5

ANALYSIS Just as in the example with pointers, two variables are declared on line 10 and their values are printed on line 12. On line 13, the function swap() is called, but note that x and y, not their addresses, are passed. The calling function simply passes the variables.
When swap() is called, program execution jumps to line 18, where the variables are identified as references. Their values are printed on line 22, but note that no special operators are required. These are aliases for the original values, and can be used as such.

On lines 24-26, the values are swapped, and then they’re printed on line 28. Program execution jumps back to the calling function, and on line 14, the values are printed in main(). Because the parameters to swap() are declared to be references, the values from main() are passed by reference, and thus are changed in main() as well.

References provide the convenience and ease of use of normal variables, with the power and pass-by-reference capability of pointers!

Back to Index