Разница между const int*, const int * const и int * const?
Я всегда путаюсь, как правильно использовать const int *
, const int * const
и int * const
. Существенно ли их различие? Существуют ли какие-то правила, определяющие, что можно, а что нельзя делать с этими указателями?
Мне нужно знать все "можно" и "нельзя" в контексте присваивания, передачи в функции и т.д.
5 ответ(ов)
Я думаю, что на этот вопрос уже достаточно ответили, но хотел бы добавить, что стоит быть осторожными с typedef
! Это не просто замены текста.
Например:
typedef char *ASTRING;
const ASTRING astring;
Тип astring
— это char * const
, а не const char *
. Это одна из причин, по которой я всегда стараюсь помещать const
справа от типа, а не в начале.
Ваша просьба о переводе на русский язык в стиле ответа на StackOverflow.com:
Константная ссылка:
Константная ссылка - это ссылка на переменную (в данном случае int), которая является константой. Мы передаем переменную по ссылке главным образом потому, что ссылки занимают меньше памяти, чем сами значения. Однако это создает побочный эффект: ссылка становится аналогом самой переменной. Мы можем случайно изменить основную переменную через доступ к ее alias, поэтому мы делаем ее константной, чтобы предотвратить этот эффект.
int var0 = 0; const int &ptr1 = var0; ptr1 = 8; // Ошибка var0 = 6; // OK
Константные указатели:
Как только константный указатель указывает на переменную, он больше не может указывать на какую-либо другую переменную.
int var1 = 1; int var2 = 0; int *const ptr2 = &var1; ptr2 = &var2; // Ошибка
Указатель на константу:
Указатель, через который нельзя изменить значение переменной, на которую он указывает, называется указателем на константу.
int const * ptr3 = &var2; *ptr3 = 4; // Ошибка
Константный указатель на константу:
Константный указатель на константу - это указатель, который не может ни изменить адрес, на который он указывает, ни изменить значение, хранящееся по этому адресу.
int var3 = 0; int var4 = 0; const int * const ptr4 = &var3; *ptr4 = 1; // Ошибка ptr4 = &var4; // Ошибка
Если у вас есть дополнительные вопросы, не стесняйтесь спрашивать!
Простое использование const
.
Самый простой способ использования const
— это объявление именованной константы. Для этого вы объявляете константу так, как будто это переменная, но добавляете const
перед ней. Необходимо сразу же инициализировать ее в конструкторе, потому что, конечно, вы не можете установить значение позже, так как это привело бы к изменению константы. Например:
const int Constant1 = 96;
Это создаст целочисленную константу с незамысловатым названием Constant1
и значением 96.
Такие константы полезны для параметров, которые используются в программе, но не должны изменяться после компиляции программы. У них есть преимущество перед директивой C-препроцессора #define
, так как они понимаются и обрабатываются самим компилятором, а не просто заменяются в тексте программы препроцессором до того, как она попадет в основной компилятор, что делает сообщения об ошибках более информативными.
Также const
можно использовать с указателями, но нужно быть внимательным, чтобы определить, является ли константой сам указатель, то, на что он указывает, или и то, и другое. Например:
const int * Constant2;
объявляет, что Constant2
— это изменяемый указатель на константное целое число, а:
int const * Constant2;
является альтернативным синтаксисом, который выполняет ту же задачу. В то время как
int * const Constant3;
объявляет, что Constant3
— это константный указатель на изменяемое целое число, и
int const * const Constant4;
объявляет, что Constant4
— это константный указатель на константное целое число. В общем, const
применяется к тому, что находится слева от него (если ничего нет, то к тому, что справа).
Это просто, но с некоторыми нюансами. Обратите внимание, что мы можем применять квалификатор const
к любому типу данных (int
, char
, float
и т.д.).
Давайте рассмотрим следующие примеры.
const int *p
⇒ *p
является только для чтения [p
— указатель на константное целое число].
int const *p
⇒ *p
является только для чтения [p
— указатель на константное целое число].
int *p const
⇒ Ошибка. Компилятор выдает синтаксическую ошибку.
int *const p
⇒ p
является только для чтения [p
— константный указатель на целое число]. Так как указатель p
здесь является только для чтения, объявление и определение должны находиться в одном месте.
const int *p const
⇒ Ошибка. Компилятор выдает синтаксическую ошибку.
const int const *p
⇒ *p
является только для чтения.
const int *const p
⇒ *p
и p
являются только для чтения [p
— константный указатель на константное целое число]. Так как указатель p
здесь является только для чтения, объявление и определение должны находиться в одном месте.
int const *p const
⇒ Ошибка. Компилятор выдает синтаксическую ошибку.
int const int *p
⇒ Ошибка. Компилятор выдает синтаксическую ошибку.
int const const *p
⇒ *p
является только для чтения и эквивалентно int const *p
.
int const *const p
⇒ *p
и p
являются только для чтения [p
— константный указатель на константное целое число]. Так как указатель p
здесь является только для чтения, объявление и определение должны находиться в одном месте.
Синтаксис объявления в C и C++ многократно описывался как неудачный эксперимент самими его создателями.
Вместо этого давайте назовём тип "указатель на Type
"; я буду называть его Ptr_
:
template< class Type >
using Ptr_ = Type*;
Теперь Ptr_<char>
— это указатель на char
.
Ptr_<const char>
— это указатель на const char
.
А const Ptr_<const char>
— это const
указатель на const char
.
Почему следует использовать указатель вместо самого объекта?
Почему в макросах используются, казалось бы, бессмысленные операторы do-while и if-else?
В чем разница между #include <filename> и #include "filename"?
Что такое Правило трёх?
Что означает 'const' в конце объявления метода класса?