Разница между const int*, int * const и const int * const в C++
Как читать объявления указателей с const: правило «от переменной к типу»
Вы почти всё поняли правильно! Есть простое мнемоническое правило: читайте объявление справа налево, начиная от имени переменной. Ключевое слово const применяется к тому, что стоит слева от него (кроме случая, когда const стоит самым первым — тогда оно применяется к типу справа). Давайте разберём каждый случай.
1. const int* ptr — указатель на константное целое
Читаем справа налево от ptr: * — «указатель», затем const int — «константное целое». Итог: ptr — это указатель на const int. Вы не можете изменить значение по адресу через этот указатель, но можете изменить сам указатель (перенаправить его на другой объект).
int a = 10, b = 20;
const int* ptr = &a;
// *ptr = 30; // Ошибка: попытка изменить const-данные
ptr = &b; // OK: указатель теперь смотрит на b
2. int* const ptr — константный указатель на целое
Читаем: ptr — это const (константный) * (указатель) на int. Итог: ptr — это константный указатель на int. Вы не можете изменить сам указатель (он всегда указывает на один и тот же адрес), но можете изменять данные по этому адресу.
int a = 10, b = 20;
int* const ptr = &a;
*ptr = 30; // OK: меняем значение a на 30
// ptr = &b; // Ошибка: указатель константный
3. const int* const ptr — константный указатель на константное целое
Читаем: ptr — это const (константный) * (указатель) на const int. Итог: ptr — это константный указатель на const int. Вы не можете ни изменить сам указатель, ни изменить данные, на которые он указывает.
int a = 10, b = 20;
const int* const ptr = &a;
// *ptr = 30; // Ошибка: данные const
// ptr = &b; // Ошибка: указатель const
Универсальный способ запомнить
- Если
constстоит слева от*(или в самом начале объявления) — защищены данные, на которые указывает указатель. - Если
constстоит справа от*— защищён сам указатель (его адрес).
Когда что использовать?
const int*— для функций, которые принимают данные только для чтения. Например:void print(const int* data, size_t size);. Это даёт гарантию, что функция не изменит ваши данные.int* const— редко используется сам по себе, но полезен в структурах данных (например, в итераторах с фиксированным адресом) или когда нужно зафиксировать адрес буфера.const int* const— для абсолютно неизменяемых указателей на неизменяемые данные. Часто применяется в embedded-программировании для указателей на регистры или константные таблицы.
Ещё один трюк: если вы видите int const* — это то же самое, что const int*. Просто читайте слева направо от const: «константное целое, на которое указывает указатель». Надеюсь, теперь стало понятнее!
Альтернативный подход: визуализация и аналогия
Можно представить указатель как коробку с адресом, а данные — как содержимое другой коробки. const — это «пломба», которая запрещает изменения.
const int* ptr— пломба на содержимом (коробка с адресом открыта, можно менять адрес, но нельзя трогать то, что лежит внутри).int* const ptr— пломба на коробке с адресом (адрес зафиксирован, но содержимое можно менять).const int* const ptr— две пломбы: и адрес, и содержимое нельзя трогать.
Ещё один полезный приём: используйте using или typedef для сложных объявлений:
using ConstDataPtr = const int*; // указатель на const int
using FixedPtr = int* const; // константный указатель на int
using ConstEverything = const int* const; // всё константно
В современном C++ (C++11 и новее) старайтесь использовать const как можно чаще — это помогает компилятору оптимизировать код и предотвращает случайные изменения. Для параметров функций предпочитайте const int* вместо передачи по значению, если размер данных велик. И помните: const — это не враг, а ваш лучший друг в написании надёжного кода!