10.15 – Указатели и const
Указание на константные переменные
Пока что все указатели, которые вы видели, были неконстантными указателями на неконстантные значения:
int value{ 5 };
int* ptr{ &value };
*ptr = 6; // меняем value на 6
Однако что произойдет, если значение value
будет константным?
const int value{ 5 }; // value - константа
int* ptr{ &value }; // ошибка компиляции: невозможно преобразовать const int* в int*
*ptr = 6; // меняем value на 6
Приведенный выше фрагмент не будет компилироваться – мы не можем установить неконстантный указатель на константную переменную. В этом есть смысл: константная переменная – это переменная, значение которой нельзя изменить. Гипотетически, если бы мы могли установить неконстантный указатель на константное значение, тогда мы могли бы выполнить косвенное обращение через неконстантный указатель и изменить значение. Это нарушит назначение const
.
Указатель на константное значение
Указатель на константное значение – это (неконстантный) указатель, указывающий на константное значение.
Чтобы объявить указатель на константное значение, используйте ключевое слово const
перед типом данных:
const int value{ 5 };
const int* ptr{ &value }; // это нормально, ptr - это неконстантный указатель,
// указывающий на "const int"
*ptr = 6; // не допускается, мы не можем изменить константное значение
В приведенном выше примере ptr
указывает на const int
.
Пока всё хорошо, правда? Теперь рассмотрим следующий пример:
int value{ 5 }; // value - не константа
const int* ptr{ &value }; // это всё еще нормально
Указатель на константную переменную может указывать на неконстантную переменную (например, переменная value
в приведенном выше примере). Подумайте об этом так: указатель на константную переменную обрабатывает переменную как константу, когда к ней обращаются через указатель, независимо от того, была ли переменная изначально определена как константа или нет.
Таким образом, допустимо следующее:
int value{ 5 };
const int* ptr{ &value }; // ptr указывает на "const int"
value = 6; // value не является константой при
// доступе через неконстантный идентификатор
Но недопустимо следующее:
int value{ 5 };
const int* ptr{ &value }; // ptr указывает на "const int"
*ptr = 6; // ptr рассматривает свое значение как const,
// поэтому изменение value через ptr недопустимо
Поскольку указатель на константное значение сам по себе не является константой (он просто указывает на константное значение), указатель может быть перенаправлен на другие значения:
int value1{ 5 };
const int *ptr{ &value1 }; // ptr указывает на const int
int value2{ 6 };
ptr = &value2; // нормально, теперь ptr указывает на другой const int
Константные указатели
Мы также можем сделать сам указатель константным. Константный указатель – это указатель, значение которого не может быть изменено после инициализации.
Чтобы объявить константный указатель, используйте ключевое слово const
между звездочкой и именем указателя:
int value{ 5 };
int* const ptr{ &value };
Как и обычная константная переменная, константный указатель должен быть инициализирован значением при объявлении. Это означает, что константный указатель всегда будет указывать на один и тот же адрес. В приведенном выше случае ptr
всегда будет указывать на адрес value
(пока ptr
не выйдет из области видимости и не будет уничтожен).
int value1{ 5 };
int value2{ 6 };
int* const ptr{ &value1 }; // хорошо, константный указатель инициализируется адресом value1
ptr = &value2; // недопустимо, после инициализации константный указатель изменить нельзя
Однако, поскольку значение, на которое указывается, всё еще не является константой, его можно изменить с помощью косвенного обращения через константный указатель:
int value{ 5 };
int* const ptr{ &value }; // ptr всегда будет указывать на value
*ptr = 6; // разрешено, поскольку ptr указывает на неконстантный int
Константный указатель на константное значение
Наконец, можно объявить константный указатель на константное значение, используя ключевое слово const
и перед типом, и перед именем переменной:
int value{ 5 };
const int* const ptr{ &value };
Константный указатель на константное значение нельзя установить так, чтобы он указывал на другой адрес, а также нельзя изменить значение, на которое он указывает, с помощью косвенного обращения через указатель.
Резюме
Подводя итог, вам нужно запомнить всего 4 правила, и они довольно логичны:
- Неконстантный указатель может быть перенаправлен на другие адреса.
- Константный указатель всегда указывает на один и тот же адрес, и этот адрес нельзя изменить.
- Указатель на неконстантное значение может изменить значение, на которое он указывает. Он не может указывать на константное значение.
- Указатель на константное значение обрабатывает значение как
const
(даже если это не так) и, следовательно, не может изменить значение, на которое он указывает.
Сохранение правильного синтаксиса объявления может быть сложной задачей. Просто помните, что тип значения, на который указывает указатель, всегда находится в самом левом краю:
int value{ 5 };
// ptr1 указывает на "const int", поэтому это указатель на константное значение
const int* ptr1{ &value };
// ptr2 указывает на "int", поэтому это константный указатель на неконстантное значение.
int* const ptr2{ &value };
// ptr3 указывает на "const int", поэтому это константный указатель на константное значение.
const int* const ptr3{ &value };
Заключение
Указатели на константные значения в основном используются в параметрах функций (например, при передаче массива в функцию), чтобы гарантировать, что функция случайно не изменит переданный аргумент. Мы обсудим это далее в разделе, посвященном функциям.