5.1. Pointers

People make a big deal out of pointers. They really aren’t that hard to understand. We already know that a variable stores some value:

double euler = 2.718281828459;

We use the name euler to retrieve the value.

A pointer is simply a variable that stores an address:

double* e_pointer = &euler;

The value assigned to e_pointer is the address of the variable euler.

While some of the memory in a running program is stored in a small number of registers, these live on the CPU chip and perform specialized functions like keeping track of the location of the next machine code instruction to execute. Most of the memory of interest to programmers is main memory, which (mostly) lives outside the CPU chip and which stores the code and data of a running program. Main memory is partitioned within a program into the following areas, which we have seen in the section Introduction to functions.

Typical program memory layout

Each part of a program: functions, variables, and objects are stored in main memory and each is assigned a unique address. When the CPU wants to fetch a value from a particular location in main memory, it must supply the address. Frequently, we don’t need to concern ourselves with the address of a value. Instead, we use a variable or function name and the CPU resolves the address for us. Sometimes we need more control, and then we use pointers to store memory addresses and manipulate them like any other variable. In C++, they are defined as a family of types that can be passed as arguments, stored in variables, returned from functions, etc.

5.1.1. Declaring pointers

A pointer is defined by the operator * and a type. Both are required because a pointer can be thought of as a compound type. The * declares that the system should store an initial memory address, not a value. The type instructs the system how many bytes after the initial location need to be allocated for storage of the value pointed to. int* int_pointer; defines a new, uninitialized pointer to an int.

Both int * int_pointer; and int *int_pointer; declare the exact same variable. White space does not matter and the last variation is common. Personally, I prefer int* p, because it emphasizes (for me) that the type is integer pointer. Declarations of pointers to other fundamental types follow a similar pattern:

double* dbl_pointer; is an uninitialized pointer to a double.

char* char_ptr; is an uninitialized pointer to a char.

Each of these declarations creates a variable of either 4 or 8 bytes, depending on the architecture. On any given CPU all pointers are the same size, regardless of what they point to - because the only thing a pointer ever stores is an address. The pointer variable stores a specific memory location (the address) and the value associated with the pointer is stored in one or more bytes starting at the pointer address.

Like any other variable in C++, an uninitialized pointer will initially contain garbage — in this case, the address of a location that might or might not contain something important. To initialize a pointer, you assign it the address of something that already exists. If you already have an object, you can use the address of operator &:

int main() {
  int n = 5; // a stack int
  int* p;    // a pointer to an int
  p = &n;    // p now points to n
}

As you might expect, you do not need to declare pointers uninitialized. You can declare and initialize in a single step. Once you have an initialized pointer, use the dereference operator * to get the value stored in the pointer, or to assign a new value.

int main() {
   int x  = 10;
   int* p = &x;    // assign the address of x to p
   *p     = 7;     // x is now 7, p is unchanged

   int x2  = *p;   // assign the value of x to new int x2
   int* p2 = &x2;  // get a pointer to another int

   p2 = p;         // p2 and p both point to x
   p  = &x2;       // make p point to another object
}

The equivalent example for references is:

int main() {
   int y  = 10;
   int& r = y;    // the & is in the type, not in the initializer
   r      = 7;    // assign to y through reference r

   int  y2 = r;   // read y through r (no * needed)
   int& r2 = y2;  // get a reference to another int

   r2  = r;       // the value of y is assigned to y2
   //r = &y2;     // error: you can't change the value of a reference
                  //   (no assignment from int* to an int&)
}

More to Explore

You have attempted of activities on this page