const T* and T* const
The variable a in this program is declared without const, yet the compiler does not let the pointer p change it:
int main() {
int a = 1;
int b = 2;
const int* p = &a;
*p = 5; // ✗
p = &b;
return *p;
}error: read-only variable is not assignableThe error is only on the line *p = 5;, while the line p = &b; compiles. What const forbids in a pointer declaration depends on where it stands.
Pointer to const
const int* p is a pointer to const. The value can be read through it but not changed. It 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. Such a declaration promises that the function will not change the numbers. An accidental write such as data[0] = 0; in the body fails with the same error as *p = 5;. This way the compiler makes sure that the function does not change the caller's numbers.
Const pointer
When const comes after the *, the pointer itself becomes constant. int* const q is a const pointer: it cannot be pointed elsewhere, so it is initialized right away. The value, though, can be changed through q. const int* const r forbids both.
#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
}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 int that is const".
Common mistake
The address of a const variable is stored in a plain pointer:
int main() {
const int limit = 10;
int* p = &limit; // ✗
return *p;
}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;.