Namespaces
Variants
Actions

Pointers

From cppreference.com


In general, pointer is a type of a variable that stores a link to another object. In C and C++, the link is the address of that object in the program memory. Pointers allow to refer to the same object from multiple locations of the source code without copying the object. Also, the same pointer variable may refer to different objects during its lifetime.

A similar, more restricted type is a reference.

Contents

Creation

A pointer is declared just like a variable but with * after the type:

int* px;

This is a pointer, which can point to an integer. The pointer is not yet initialized, therefore it points to an undefined location and any access to it is undefined behaviour.

What the statement did behind the scenes is to reserve a memory area large enough to hold a memory address on the given system.

Notice that so far we didn't reserve any memory to hold the integer object that the pointer will point.

Using pointers

Assignment and access of pointers:

int a = 1;
int b;
int* px;
 
px = &a;   /* point pointer to variable a */
b = *px;   /* copy value where px points to into b */
 
a = 10;
 
std::cout << a;     /* 10 */
std::cout << *px;   /* 10 */
std::cout << b;     /*  1 */

The code above takes the address of a (& operator) and assigns it to the pointer px. Afterwards we retrieve the value, by so called dereferencing of the pointer, using the * operator and assign it to b.

In the example you see that px points to the memory of variable a and therefore will see any changes made to a. b however has a copy of the value of a and therefore doesn't change.

int a = 10;
int* px;
int* py;
 
px = &a;   /* px points to memory of a */
py = px;   /* py points to memory of a */

Pointer can be assigned to other pointers of the same type just like variables. In this case the address inside the pointer is copied and both pointer point to the same location after the assignment.


Using pointers to objects

If pointers refer to objects which are not plain old data types, there is an easier syntax to access the members of the object.

SomeObject a;
SomeObject* p_obj = &a;
 
(*p_obj).do_something(); /* works, but nicer syntax below */
p_obj->do_something();   /* accessing function of object */
p_obj->i_variable = 1;   /* accessing variable of object */

Accessing members of objects could be done by dereferencing the pointer and accessing the function or variable of the object using the . operator. Or simply do both in a nicer syntax using the -> operator.

Special null pointer

Sometimes it is desirable to mark a pointer as invalid. This can be achieved by the assignment of nullptr to the pointer. This pointer is called a null pointer and dereferencing it results in undefined behavior.

int* px = nullptr;

A special feature of a null pointer is that it is the only possible pointer value that evaluates to false when converted to bool.

Before the C++11 standard, the standard way to refer to null pointers was by using NULL macro constant. Unfortunately, this method has a serious issue of being type-unsafe as NULL evaluates to an integer, not a pointer.

Consider the following example:

#include <cstddef>
#include <iostream>
 
void foo(char*)
{
    std::cout << "foo(int*);\n";
}
 
void foo(long)
{
    std::cout << "foo(long);\n";
}
 
int main()
{
    foo(nullptr);
    foo(NULL);
}

Output:

foo(int*);
foo(long);

It is obvious that the intent of the programmer was to call the first overload of the foo function in both cases. However, as NULL is an integral constant, the second overload is called, which is unexpected behavior and may lead to a bug.

Pointers and const

Since pointers access memory of a certain type, like variables, and are variables themselves they have two const parameters.

int a;
const int b = 1;
 
/* pointer is constant */
int* const px = &a;
 
/* value the pointer points to is constant */
const int* px = &b;
int const* px = &b;  // exact same meaning as line above
 
/* pointer and value is constant */
const int* const px = &b;
int const* const px = &b;

If the pointer is const, we can't assign a different address to it. The pointer will always point to the same part of memory.

If the value is constant, we are able to assign a different address to the pointer, but we can't change the value it points to.

Pitfalls

Accessing uninitialized pointers

Pointers should always be initialized with a valid address or nullptr. But this doesn't prevent access to null pointers causing an exception or undesired behaviour.

Therefore pointers should be compared against nullptr before access, unless it is guaranteed that the pointer is valid.

int* px = nullptr;
 
/* some other code which might set px */
 
if (nullptr != px) {
    /* access px */
} else {
    /* maybe error handling code */
}

Different lifetime of memory and pointer

void func() 
{
 
    int a;
    int* px;
 
    px = sub_func();
 
    a = *px;   /* access to invalid memory address */
}
 
int* sub_func() 
{
    int x = 10;
    return &x;
}

SubFunc creates a variable and returns the pointer to the variable. When SubFunc returns, the variable x goes out of scope and its memory is freed. The pointer px will still point to the old address of x which might already be assigned to a different process. Accessing the address is undefined behaviour.

int a = 0;
int* px = new int(0);
 
px = &a;  /* access to int reserved above is lost */

Memory for an integer is reserved using new. First px points to this memory, but later on it points to the memory of a. The reserved memory is still reserved, but the address is lost, because no pointer points to it any more.