News & UpdatesProgrammingWeb programming StoreMy Projects

C++ Tutorial – 06 – Pointers

A pointer is a variable that contains the memory address of another variable, called the pointee.

Creating pointers

Pointers are declared as any other variable, except that an asterisk (*) is placed between the data type and the pointer’s name. The data type used determines what type of memory it will point to.

int* p; // pointer to an integer
int *q; // alternative syntax

A pointer can point to a variable of the same type by prefixing that variable with an ampersand, in order to retrieve its address and assign it to the pointer. The ampersand is known as the address-of operator (&).

int i = 10;
p = &i; // address of i assigned to p

Dereferencing pointers

The pointer above now contains the memory address to the integer variable. Referencing the pointer will retrieve this address. To obtain the actual value stored in that address the pointer must be prefixed with an asterisk, known as the dereference operator (*).

std::cout << "Address of i: " <<  p; // ex. 0017FF1C
std::cout << "Value of i: "   << *p; // "10"

When writing to the pointer, the same method is used. Without the asterisk the pointer is assigned a new memory address, and with the asterisk the actual value of the variable pointed to will be updated.

p = &i;  // address of i assigned to p
*p = 20; // value of i changed through p

If a second pointer is created and assigned the value of the first pointer it will then get a copy of the first pointer’s memory address.

int* p2 = p; // copy of p (copies address stored in p)

Pointing to a pointer

Sometimes it can be useful to have a pointer that can point to another pointer. This is done by declaring a pointer with two asterisks and then assigning it the address of the pointer that it will reference. This way when the address stored in the first pointer changes, the second pointer can follow that change.

int** r = &p; // pointer to p (assigns address of p)

Referencing the second pointer now gives the address of the first pointer. Dereferencing the second pointer gives the address of the variable and dereferencing it again gives the value of the variable.

std::cout << "Address of p: " << r;  // ex. 0017FF28
std::cout << "Address of i: " << *r; // ex. 0017FF1C
std::cout << "Value of i: "   << **r;// 20

Dynamic allocation

One of the main usages of pointers is to allocate memory during run-time – so called dynamic allocation. In the examples so far, the programs have only had as much memory available as has been declared for the variables at compile-time. This is referred to as static allocation. If any additional memory is needed at run-time, the new operator has to be used. This operator allows for dynamic allocation of memory, which can only be accessed through pointers. The new operator takes either a primitive data type or an object as its argument, and it will return a pointer to the allocated memory.

int* d = new int; // dynamic allocation

An important thing to know about dynamic allocation is that the allocated memory will not be released like the rest of the program memory when it is no longer required. Instead, it has to be manually released with the delete keyword. This allows you to control the lifetime of a dynamically allocated object, but it also means that you are responsible for deleting it once it is no longer needed. Forgetting to delete memory that has been allocated with the new keyword will give the program memory leaks, because that memory will stay allocated until the program shuts down.

delete d; // release allocated memory

Null pointer

A pointer should be set to zero when it is not assigned to a valid address. Such a pointer is called a null pointer. Doing this will allow you to check whether the pointer can be safely dereferenced, because a valid pointer will never be zero.

For example, although the previous pointer has had its memory released, its stored address still points to a now inaccessible memory location. Trying to dereference such a pointer will cause a run-time error. To help prevent this, the deleted pointer should be set to zero. Note that trying to delete an already deleted null pointer is safe. However, if the pointer has not been set to zero, attempting to delete it again will cause memory corruption and possibly crash the program.

d = 0;    // mark as null pointer
delete d; // safe

Since you may not always know whether a pointer is valid, a check should be made whenever a pointer is dereferenced to make sure that it is not zero.

if (d != 0) { *d = 10; } // check for null pointer

The constant NULL can also be used to signify a null pointer. NULL is typically defined as zero in C++, making the choice of which to use a matter of preference. The constant is defined in the stdio.h standard library file, which is included through iostream.

#include <iostream>
// …
if (d != NULL) { *d = 10; } // check for null pointer
Recommended additional reading:
Sams - Teach Yourself C++ in One Hour a Day