const T* и T* const
Переменная a в этой программе объявлена без const, и всё же изменить её через указатель p компилятор не даёт:
int main() {
int a = 1;
int b = 2;
const int* p = &a;
*p = 5; // ✗
p = &b;
return *p;
}error: read-only variable is not assignableОшибка только в строке *p = 5;, а строка p = &b; компилируется. Что запрещает const в объявлении указателя, зависит от того, где он стоит.
Указатель на константу
const int* p — указатель на константу (pointer to const). Через него значение можно читать, но нельзя менять. Указывать он может и на переменную без const: запрет действует только на запись через p, а a = 5; по-прежнему работает. Сам p — обычная переменная, поэтому его можно перенаправить, как в строке p = &b;.
Функция, которая только читает числа по указателю, объявляет параметр как const int* data. Такое объявление обещает, что функция не изменит числа. Случайную запись вроде data[0] = 0; в теле функции компилятор остановит той же ошибкой, что и *p = 5;. Так компилятор следит, чтобы функция не изменила числа вызывающего кода.
Константный указатель
Если const стоит после *, постоянным становится сам указатель. int* const q — константный указатель (const pointer): перенаправить его нельзя, поэтому инициализируют его сразу. Зато значение через q менять можно. В const int* const r запрещено и то и другое.
#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
}Всё решает место const относительно *: слева от звёздочки он относится к значению, а справа — к самому указателю. Поэтому int const* p значит то же, что const int* p.
Объявление удобно читать справа налево, начиная с имени. int* const q — «q — константный указатель на int». const int* p — «p — указатель на int, который const».
Частая ошибка
Адрес const-переменной кладут в обычный указатель:
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 *'Через int* значение limit можно было бы изменить, поэтому компилятор запрещает такую инициализацию. Объявите указатель как const int* p = &limit;.