Previous Table of Contents Next


Chapter 3
Pointers, Strings, and Things

One of the things that most distinguish C and C++ from most other computer languages is their use of pointers. At first, pointers may seem difficult or exotic—but other languages use pointers all the time. The difference is that these other languages carry out pointer operations under the covers—you just don’t see them. C and C++ are more transparent, and they give you more control.

What are pointers, anyway? I’ll provide a more detailed description in the next section, but, in general, pointers are addresses: relatively small units of data (two bytes on 16-bit systems) that access other pieces of data indirectly. You can use an address to access an arbitrarily large chunk of data, often making significant gains in efficiency. In C and C++, pointers are closely related to arrays and character strings.

Pointers are, if anything, even more important in C++ than in C. C++ encourages the dynamic creation of objects through the use of the new operator, which returns a pointer. Moreover, certain operator functions (which I’ll discuss in Chapter 7, “Operator Overloading”) require understanding of pointers. In this chapter, we’ll focus on the use of pointers with parameters, strings, and arrays.

A More Efficient Way to Pass Data

Simply stated, a pointer is a variable or an argument that stores an address in memory. Although this definition is technically complete, it is probably meaningless unless you understand what pointers are for—that is, how they are used. The important point is that, like a handle or a key, a pointer provides indirect access to data, and sometimes this arrangement is much more efficient than doing things the usual way.

Suppose you and I are two different functions (ok, I know that sounds silly, but stay with me on this) or, possibly, two separate objects. Suppose, further, that you have a large piece of data that you need to share with me. For example, you might need to pass me a long string of characters for me to display on the screen.

There are several possibilities for enabling this sharing of data. If the data structure is global, then sharing of data is automatic. But as I explained in Chapter 2, it’s best to use as few global variables as possible. So you frequently need to pass a data structure as an argument in a function call. This leaves two possibilities:

  You could pass the data structure by giving me a copy of the entire data structure. This approach is fine in the case of small data types such as simple integers (int, short, and long) but can be inefficient when you start dealing with arrays, strings, and other large objects.
  You could give me the address of the data structure. An address (that is, a pointer) looks just like an integer, but it has a special meaning to the computer: an address tells the processor where to find something in memory.

To visualize these scenarios, try to picture the computer’s stack, which is the area of memory reserved for passing data between functions. Passing a data structure directly causes an entire copy of the data to be placed on the stack. But passing an address causes a relatively small amount of data to be placed on the stack (typically, either two or four bytes, depending on the computer’s address size). As the function being called, I need only the location of the data structure—its address—to gain access to all the data. Figure 3.1 illustrates these two approaches to passing data.


Figure 3.1  Passing data directly and passing its address (a pointer).

In the second scenario, I have only the address of the data. But this is all I need to access or modify the original data. Manipulating data through its address is called indirection. Modern processors are designed with a great deal of support for indirection.

The usefulness of pointers is not limited to saving memory on the stack. Among other things, pointers enable passing by reference, which is the topic we’ll look at next.

Pointers and Passing by Reference

If you’ve used Basic, FORTRAN, or Pascal, to name only a few common languages, you’re probably familiar with passing by reference and passing by value: a function can permanently change the value of an argument only if you pass by reference.

With languages such as Basic and FORTRAN, passing by reference is automatic or easily controlled by a simple keyword (such as “var” in Pascal). You may not have realized it, but these languages pass a pointer when you pass by reference. The language hides this fact from you, so you don’t have to learn pointer syntax, as required by C.

C++, but not C, provides a special reference operator (&) that lets you use the same high-level pass-by-reference technique that Basic, FORTRAN, and Pascal support. This operator is introduced in Chapter 6, “Another Look at Constructors.” However, it’s best to learn first how to pass by reference the hard way: with pointers. Even with C++, sooner or later you have to use pointers, and the best way to learn is to start with passing by reference.

The connection between pointers and passing by reference should be clear if you look at Figure 3.1 again. If you pass by value, I get a complete copy of the original data. I can change the copy as much as I want, but this has no effect on the original data. When I finish execution, all the stack area I’ve been using is released. None of the changes I made to my copy have any effect on the program.

If, however, I get a pointer to your data, then changes I make affect you. A pointer is not exactly a new piece of data but instead is something that tells me the location of your data, which enables me to change it. It’s as if you gave me the location and combination of the file cabinet containing your original records rather than make separate copies for me.

Therefore, passing a pointer (an address) is the same as passing by reference. In fact, that’s literally what passing by reference means: a pointer refers to the original copy of the data.

Steps for Passing by Reference

To use pointers to pass by reference, you need to follow several steps:

1.  In the prototype and function-definition argument list, declare the parameter using the pointer-indirection operator (*).
2.  When passing an argument, make sure to pass an address. Typically, this means applying the address operator (&) to your argument.
3.  Within the function definition, apply the pointer-indirection operator (*) when you want to access the data pointed to.

The rest of this section discusses each of these steps in a more detail.


Previous Table of Contents Next