Avatar ·

Разница между const int*, const int * const и int * const в C++

Привет! Я только начал изучать C++ и пытаюсь разобраться с указателями и квалификатором const. Прочитал несколько статей, но всё равно путаюсь в этих трёх конструкциях: `const int*`, `int * const` и `const int * const`. Например, я написал такой код: ```cpp int value = 5; const int* ptr1 = &value; int * const ptr2 = &value; const int * const ptr3 = &value; ``` Я понимаю, что все они как-то связаны с тем, что данные или сам указатель нельзя менять, но не могу чётко сформулировать разницу. Когда я пытаюсь присвоить новое значение через `*ptr1 = 10;`, компилятор ругается. А если я попробую изменить `ptr2`, например `ptr2 = &otherValue;`, тоже ошибка. Но `ptr1` я могу перенаправить на другую переменную, а `ptr2` — нет. Почему? Также хочу понять, в каких случаях какой вариант использовать. Может быть, есть какое-то мнемоническое правило, чтобы запомнить? Буду благодарен за подробное объяснение с примерами. <<>>

Ключ к пониманию: читайте справа налево

Лучший способ запомнить разницу — читать объявление указателя справа налево. Ключевое слово const применяется к тому, что стоит непосредственно слева от него (или справа, если слева ничего нет).

const int*  // Читаем: * (указатель) на int, который const
int * const // Читаем: const (константный) * (указатель) на int
const int * const // Читаем: const * (константный указатель) на int, который const

1. const int* — указатель на константное целое

Сам указатель можно менять (перенаправлять на другой объект), но нельзя изменять значение объекта, на который он указывает (через разыменование).

int value1 = 5;
int value2 = 10;
const int* ptr = &value1;

*ptr = 20;  // ОШИБКА: нельзя изменить значение через ptr
ptr = &value2; // ОК: сам указатель можно перенаправить

// Но! Если value1 не const, его можно изменить напрямую:
value1 = 25; // ОК

2. int * const — константный указатель на целое

Сам указатель нельзя перенаправлять (он всегда указывает на один и тот же адрес), но значение объекта можно менять (если объект не const).

int value1 = 5;
int value2 = 10;
int * const ptr = &value1;

*ptr = 20;  // ОК: меняем значение через указатель
ptr = &value2; // ОШИБКА: нельзя изменить сам указатель

3. const int * const — константный указатель на константное целое

Сочетает оба ограничения: ни сам указатель, ни значение через него изменить нельзя.

int value1 = 5;
int value2 = 10;
const int * const ptr = &value1;

*ptr = 20;  // ОШИБКА
ptr = &value2; // ОШИБКА

Мнемоническое правило

  • const int* — "const стоит слева от *" → константны данные.
  • int * const — "const стоит справа от *" → константен указатель.
  • const int * const — константно и то, и другое.

Когда что использовать?

  • const int* — для функций, которые принимают массив/строку и обещают не менять данные (например, size_t strlen(const char* str)).
  • int * const — редко, обычно для жёсткой привязки указателя к конкретному адресу (например, в embedded-системах для регистров).
  • const int * const — для константных глобальных данных, которые не должны меняться и не должны быть перенаправлены.

На практике чаще всего встречается const int* (или int const* — это одно и то же). Остальные варианты используйте только если есть явная необходимость.

<<>>

Альтернативный взгляд: через модификацию объекта

Можно запомнить через ответ на вопрос "Что именно const?"

  • const int* — "const int" (целое константно) + "*" (указатель). То есть данные константны.
  • int * const — "int" + "* const" (константный указатель). То есть указатель константен.
  • const int * const — "const int" + "* const" = и данные, и указатель константны.

Ещё одна подсказка: попробуйте мысленно подставить слово "pointer" вместо звёздочки. Например:

const int * = const int pointer = указатель на const int
int * const = int pointer const = указатель const на int

В C++ есть ещё синтаксис int const*, который идентичен const int*. Некоторые разработчики предпочитают его, потому что правило "const применяется к тому, что слева" работает всегда: int const * — const применяется к int (данные константны). Но оба варианта корректны.

Совет: в реальном коде старайтесь избегать int * const без крайней необходимости — это часто запутывает читающих код. Используйте const int* для параметров функций и const int * const для глобальных констант-указателей.