Previous Table of Contents Next


First, you must apply the indirection operator (*) to declare your intention to use a pointer argument. The declaration is the same as it would be if the argument were not a pointer except that the indirection operator is placed to the immediate left of the argument name. For example, the following prototype declares a function taking one argument: a pointer to an int.

void double_it(int *n);

Second, you must pass an address argument. Typically, you apply the address operator (&) to a normal variable (in this case, an int). Alternatively, you can pass a pointer variable.

The following code gives an example of the first approach, that of using the address operator:

int amount;
...
double_it(&amount); // Pass address of amount.

Finally, within the function definition, use the indirection operator (once again) to access the data pointed to. For example:

*p = *p * 2;

By using multiplication assignment (*=), you could instead write:

*p *= 2;

The two operators (* and &) are inverses of each other. The pointer-indirection operator (*) means “the object pointed to by,” and the address operator says, “Get the address of.” The first operator converts an address into the contents at that address (for example, it converts a pointer to an int into an actual int); the second operator converts a variable into the address of that variable.

Using p without the indirection operator (*) affects the pointer itself and not the thing pointed to. For example, (*p)++ increments the data pointed to, whereas p++ advances the pointer to the next address.

Two Complete Pass-by-Reference Examples

Both the indirection (*) and the address (&) operators are necessary for working with pointers, as the examples in this section demonstrate. To pass by reference, you first have to put an address on the stack (which involves &); later, in the function definition, you use that address to access the data pointed to (which involves *).

Figure 3.2 shows the complete context for the example code from the previous section. The example first defines a variable, amount, and sets it to 5. After calling the double_it function and passing the address of amount, the variable contains the value 10. If double_it were passed the normal way (by value), the double_it function would have no effect.


Figure 3.2  Passing the address of amount to the double_it function.

Figure 3.3 shows an example that passes two arguments by reference. (Incidentally, you can mix types of arguments; in a long argument list, some may be pointers and others may be simple data types.) Here, the function switches the value of two variables, a and b. The function can modify both a and b because the address of each is passed, enabling the function to access a and b themselves rather than copies of a and b.


Figure 3.3  Passing addresses of two variables, a and b.

Pointers and Arrays

In C++, pointers have a close connection to arrays. By using pointers, you can often process arrays more efficiently.

Arrays are an important element of almost every computer language—certainly of the major languages commonly used today. Arrays are essential in serious programming projects for two reasons:

  They provide a convenient way to allocate arbitrarily large chunks of data in program memory.
  They can be used to perform a large number of operations with a few lines of code. By combining an array with a loop, you can perform a series of statements and then repeat the operations on an arbitrarily large chunk of memory.

The importance of arrays and loop processing cannot be overestimated. Computer programming, and computers themselves, would be far less powerful without this single aspect of programming. C++ enhances this capability by providing pointer operations.

Array Basics

Here is the C++ syntax for declaring arrays:

type name[length];

For multidimension arrays, the syntax is as follows:

type name[length1][length2]...[lengthN];

In both cases, the brackets are intended literally. In the multidimension case, the number of dimensions can be two, three, or N, where N is as high a number as you want. (Bear in mind, though, that arrays with an unreasonably high number of dimensions are likely to eat up all your computer’s memory.)

The rest of this example covers a single-dimension case, although multiple dimensions follow similar rules. The length in the declaration indicates the number of elements in the array. (For multiple arrays, the number of elements is length * length * ... length.) The lowest index is always 0, and the highest is always length-1.

A couple of examples should help make this clear. Consider the following declaration:

int a[5];

This declaration creates not one, but five integers. The variables are referred to as follows:

a[0]
a[1]
a[2]
a[3]
a[4]

Notice that the highest index value supported here is 4, which is one less than the length (5). Similarly, we can declare an array 10 elements long:

int b[10];

This declaration creates 10 integers. Again, the highest index, 9, is one less than the length (10).

b[0]
b[1]
b[2]
b[3]
b[4]
b[5]
b[6]
b[7]
b[8]
b[9]
There’s a reason I’m emphasizing that the highest index used is length-1. When you’re working with arrays, one of the most common sources of bugs is to incorrectly set the initial and terminating conditions of a loop. Even experienced programmers occasionally set the terminating condition wrong, because they forget that the highest index is length-1. So check loop conditions carefully.

When I say that a declaration such as “int a[5]” creates five integers, I mean that literally. Figure 3.4 shows how the integers are laid out sequentially in memory. This illustration assumes that each int is two bytes even though on some systems (such as 32-bit systems), the int size is larger.


Figure 3.4  An array with five integers.

Declaring an array of five integers is similar in many ways to declaring each integer separately. We could declare five integers this way:

int a0;
int a1;
int a2;
int a3;
int a4;

How is this different from declaring the five-element integer array a, which generates the elements a[0], a[1], a[2], a[3], and a[4]?

Well, most obviously, the array declaration saves effort. Declaring integers individually is not so tedious when there are only five, but think of how many lines of code are saved when there are a thousand integers:

int a[1000];

And there’s another reason. Elements can be indexed by either a constant or a variable. For example:

int a[5];

a[0] = 10;
a[1] = a[0] + 1;

int n = 2;
a[n] = a[1];    // Assign a[1] to a[2]

Using variable indexes with arrays is a powerful technique. It enables loop processing of arrays, as in the following example.

// Initialize all 1,000 elements of the array to
//  contain the value 100.

int a[1000];
int i = 0;
while (i++ < 1000)
   a[i] = 100;

Loop Processing with Pointers

Pointers provide another way to perform loop processing. The pointer version is generally more efficient than a version using array indexes. (This is particularly true with multidimension arrays.) For instance, the example at the end of the last section could be written this way:

// Initialize all 1,000 elements of the array to 100.

int a[1000];
int i = 0;
int *p = a;
while (i++ < 1000)
   *p++ = 100;

Some of these statements require a closer look. The third statement declares a pointer, p, and initializes it to the starting address of the array, a. The declaration works this way because when an array name appears without an index, it is translated into the address of the first element. This is one reason pointers and arrays are closely connected—both are address expressions.

int *p = a;

The loop statement relies on C++ rules of precedence to do things in a certain order.

while (i++ < 1000)
   *p++ = 100;


Previous Table of Contents Next