Skip to main content

Section 18.7 Why Pointers? And Why Not Pointers?

At this point (no pun intended), some fair questions would be β€œWhy do we need pointers? Aren’t they just the same as references but with trickier syntax?”
The answer to those questions is that yes, pointers do allow us to do the same things as references, but they also allow us to do things that references cannot. Some of these things are very useful, but they also allow us to get ourselves and our code into all kinds of trouble. Pointers are a powerful tool, but they can also be a source of bugs and security vulnerabilities if not used carefully.
On thing we can do with a pointer but not a reference is to change what a pointer points at. We can write int* p = &x; to make p point at x and then later write p = &y; to make it point at y by changing the address that it holds. This is not possible with references. Once a reference is assigned, it cannot be changed to refer to something else.
Another thing we can do with a pointer is to leave it uninitialized. A reference must always be initialized when you declare it. It is an error to write int& r; without assigning it to something. A pointer, on the other hand, can be declared without being initialized - int* p; This is useful if we want to create a pointer but we don’t know what it will point to yet.

Insight 18.7.1.

References were designed as a safer and easier version of a pointer. A reference automatically gets an address when it is assigned. int& r = x; automatically takes the address of x. And they automatically dereference themselves when used. r = 10; automatically dereferences the pointer to change the thing that r references to be 10. And you never have to worry about a reference being null as you cannot make a reference variable that does not actually refer to something.
The flexibility of pointers introduces some risks. It is easy to end up with a pointer that holds an address that does not make sense. If we declare a pointer, but do not initialize it, it will be pointing to an unpredictable location in memory (whatever bits were in memory where the pointer was placed will be interpreted as an address). That is the case below:
Listing 18.7.1.
In that program, line 7 declares a pointer p but does not initialize it. That reserves some space in memory for p, but does not assign anything. So the address that p holds is whatever happened to already be in memory there! When we dereference the pointer, one of two things may happen:
  1. A segmentation fault. The memory of a program is divided into segments. The operating system keeps track of these segments - what memory a program should use and what it should use each segment for. If your program tries to read memory it hasn’t been allocated, or if it tries to write to memory it should only read, the operating system stops the program. This is a segmentation fault.
  2. The address is legal for the program to make use of. In which case the program happily reads or modifies some memory that does not actually make sense to change. In this case, changing *p could change another variable or something even more important - like what location to return to after the current function is done.
Believe it or not, the first case is the better one! It is much easier to debug a program that crashes than one that runs but does not do what you expect. The second case could silently change some memory. The effects of that change may not be seen until much later in the program.
C and C++ have a reputation for allowing programmers to write unsafe code. The use of raw memory addresses are at the root of many of the problems that these languages allow for. C++ provides higher level constructs (like references) that allow us to sometimes avoid working directly with pointers, and to manage them when we do have to work with them. So wise programmers use those features whenever possible.
There are tools to help catch memory related issues. In the case of accessing an uninitialized value, like in the program above, the compiler warning flag -Wall will normally identify the problem. In most of the book it is enabled, it was intentionally disabled in the ActiveCode above so we could cause problems. (You can try copying the code from this page to another page - it should generate a warning there.) We will learn about tools for detecting more complex issues later on.

Checkpoint 18.7.1.

What are things that a pointer can do that a reference can not?
  • Change what it points at.
  • Be uninitialized.
  • Be used as an alias for another variable.
  • Both can do this.
  • Automatically dereference itself.
  • That is what references do.

Checkpoint 18.7.2.

Which of the following describe what might happen if you dereference a pointer that points to a bad address?
  • The program may crash. Or it may continue running but not do what you expect.
  • The program will always crash.
  • Not always.
  • The program will always continue running, but will not do what you expect.
  • Not always.
You have attempted of activities on this page.