Tags C & C++

Pointers can be quite confusing for newbies. They can be even more confusing when used in conjunction with const. First, let's make sure that we understand the declaration syntax of constant pointers and data.

Listing 1:

// Neither the data nor the pointer are const
//
char* ptr = "just a string";

// Constant data, non-constant pointer
//
const char* ptr = "just a string";

// Constant pointer, non-constant data
//
char* const ptr = "just a string";

// Constant pointer, constant data
//
const char* const ptr = "just a string";

There is a very nice technique to read such statements and understand what they mean immediately, without the comments. Take a look at the asterisk (*) and divide the statement to 2 parts: to the left of the asterisk and to the right of the asterisk. Now, it should be obvious to you what is constant and what isn’t. Let’s take the 3rd statement in the code Listing 1 as an example: We look to the left of the asterisk and see char, no const keyword there, thus the data is not constant. Now we look to the right of the asterisk and see const ptr. “Aha !”, we say, ptr is constant. So, we conclude, the statement means: a constant pointer to non-constant data.

Now, when we have this point clear and understood, we might be wondering what exactly “constant data” and “constant pointer” mean. In fact, this is quite simple as well: just remember that both the data and the pointer are variables (a pointer is a variable which holds the address of another variable in it). So, both “constant data” and “constant pointer” actually mean “constant variable”.

So, let's sum things up: a constant pointer to some data is a pointer that after its initialization may not point to any other data – we force the pointer to be loyal to its data. A constant data means that through the given pointer, we can not change the data (this is very useful in function arguments). Here is some code to demonstrate these concepts (note that for educational purposes I included code that will cause compilation errors, but commented it out with detailed explanations, so the code as a whole should compile).

Listing 2

#include <iostream>

using namespace std;

int main()
{
    int foo = 4;
    int bar = 16;

    // ptr - non-constant pointer, non-constant
    // data
    int* ptr = &foo;

    // OK: Data is non-constant, so it can be
    // changed via ptr
    *ptr = 6;

    // The pointer is non-constant, so it may
    // point to other data and change it
    ptr = &bar;
    *ptr = 22;

    // ptr_to_const - non-constant pointer,
    // constant data
    const int* ptr_to_const = &foo;

    // Compilation error: ptr_to_const
    // treats its data as constant, so
    // it may not change it
    // *ptr_to_const = 10;

    // OK: the pointer is non-constant,
    // so we may change it (= change
    // the address it stores)
    ptr_to_const = &bar;

    // Compilation error: ptr_to_const
    // still considers any data it
    // points to as constant
    // *ptr_to_const = 100;

    // const_ptr - constant pointer,
    // non-constant data
    int* const const_ptr = &foo;

    // OK - const_ptr treats its data as
    // non-constant
    *const_ptr = 15;

    // Compilation error - const_ptr is
    // a constant pointer so we can't
    // change its value
    // const_ptr = &bar;

    // const_ptr_to_const - constant pointer,
    // constant data
    const int* const const_ptr_to_const = &foo;

    // Compilation error: data
    // is constant
    // *const_ptr_to_const = 28;

    // Compilation error - the
    // pointer is constant
    // const_ptr_to_const = &bar;

    return 0;
}

Note: in some code you might see the keyword const come after the type, rather than before it, to denote pointers to constant data. These are just two different ways to say the same thing. So, the following declarations are equivalent:

// Declare a pointer to a constant integer
//
const int* ptr1;

// Declare a pointer to a constant integer
// (completely equivalent to ptr1)
//
int const* ptr2;