Skip to content
Theory · References and Pointers · lesson 6 of 9

const T* and T* const

A pointer is a slip with a house's address. Two different things can be forbidden on such a slip:

  • changing anything in the house: you may come and look, but not touch;
  • rewriting the slip itself: the address is written for good, though things in the house may change.

In C++, both bans are written with the same word, const. The variable a in this program is declared without const, yet the compiler does not let the pointer p change it:

C++
int main() {
    int a = 1;
    int b = 2;
    const int* p = &a;
    *p = 5;  // ✗
    p = &b;
    return *p;
}
TEXT
error: read-only variable is not assignable

The error is only on the line *p = 5;, while the line p = &b; compiles. What const forbids in a pointer declaration depends on where the const itself stands.

Pointer to const

const int* p is a pointer to const. It is a slip marked "look only":

  • the value can be read through p but not changed;
  • p may also point to a variable without const: the ban covers only writing through p, and a = 5; still works;
  • p itself is an ordinary variable, so it can be pointed elsewhere, as in the line p = &b;.

A function that only reads numbers through a pointer declares its parameter as const int* data. This way it promises not to change the caller's numbers: an accidental write such as data[0] = 0; in the body fails with the same error as *p = 5;.

Const pointer

When const comes after the *, the pointer itself becomes constant. int* const q is a const pointer. It is a slip with the address written for good:

  • q cannot be pointed elsewhere, so it is initialized right away;
  • the value can be changed through q.

const int* const r forbids both.

C++
#include <iostream>

int main() {
    int a = 1;
    int b = 2;
    const int* p = &a;
    p = &b;
    int* const q = &a;
    *q = 5;
    std::cout << *p << ' ' << a << '\n';  // 2 5
}

Let's go through the example line by line:

  1. const int* p = &a; is a "look only" slip with the address of a.
  2. p = &b; rewrites it: now p points to b.
  3. int* const q = &a; is a slip with the address of a written for good.
  4. *q = 5; changes a through q, which is allowed.
  5. The program prints 2 5: *p reads b, and a now holds 5.

Where const stands

What decides is where const stands relative to the *. To the left of the asterisk it applies to the value, and to the right it applies to the pointer itself. That is why int const* p means the same as const int* p.

A declaration is easiest to read from right to left, starting at the name:

  • int* const q reads "q is a const pointer to int";
  • const int* p reads "p is a pointer to a const int".

Common mistake

The address of a const variable is stored in a plain pointer:

C++
int main() {
    const int limit = 10;
    int* p = &limit;  // ✗
    return *p;
}
TEXT
error: cannot initialize a variable of type 'int *' with an rvalue of type 'const int *'

Through an int*, the value of limit could be changed, so the compiler rejects this initialization. Declare the pointer as const int* p = &limit;.

Key points

  • const int* p forbids changing the value through p, but p can be pointed elsewhere.
  • int* const q forbids pointing q elsewhere, but the value can be changed through it.
  • const to the left of * applies to the value, to the right of it to the pointer itself.
  • A const int* data parameter promises that the function only reads the numbers.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way