Автоматное программирование
- 1 year ago
- 0
- 0
В языках программирования
C
,
C++
,
C#
и
D
const
является квалификатором типа:
ключевое слово
применяется к
типу данных
, показывая, что данные константны (неизменяемы). Это может быть использовано при объявлении (декларировании)
констант
. Отличительная особенность
const
в C-подобных языках программирования проявляется при его комбинировании с
типами данных
, что дает сложное поведение в сочетании с указателями, ссылками, составными типами данных и при проверке типов.
При объявлении объектов с использованием const их значения константны (неизменяемы), в отличие от перeменных . Этот основной сценарий использования — объявление констант — имеет параллели во многих языках.
Однако в отличие от других языков в семье языков C
const
является частью
типа
, а не
объекта
. Например, в C
const int x = 1;
объявляет объект
x
типа
const int
, где
const
— часть типа. Это может читаться «(const int) x» — в то время как в
Ada
X : constant INTEGER := 1_
объявляет константу (вид объекта)
X
типа
INTEGER
, т. е.
constant
является частью
объекта
, а не
типа
.
Появляются два тонких момента. Во-первых,
const
может быть частью более сложных типов. Например,
int const * const x;
(выражение читается от переменной x в противоположную сторону) объявляет константный указатель на константное целое число, в то время как
int const * x;
объявляет переменную-указатель на константное целое, а
int * const x;
объявляет константный указатель на переменное целое. Во-вторых, поскольку
const
является частью типа, это используется при проверке типов. Например, следующий код некорректен:
void f(int& x);
//...
const int i;
f(i);
потому что аргумент, передаваемый в
f
, должен быть
ссылочной переменной на целое
, а
i
—
константное целое
. Требование такого соответствия — форма обеспечения правильности программы, также известное как
const
-овая правильность
(
англ.
const
-correctness
). Это дает возможность
контрактного проектирования
, где у функций в качестве части сигнатуры типа указывается, будут ли они изменять свои аргументы или нет, и изменяемы (неконстантны) ли их возвращаемые значения. Такая проверка типов интересна прежде всего для указателей и ссылок (т. е. когда параметры передаются по ссылке) — а не для основных типов, таких как целые, — а также для составных типов данных или шаблонизированных типов, таких как
контейнеры
. Вследствие
неявного преобразования типов
при выполнении программы
const
может быть опущен.
При сохранении в
памяти компьютера
, константность не накладывает на
значение
ограничения на запись.
const
— это скорее конструкция времени компиляции, которую программист потенциально может использовать, однако это необязательно. Стоит обратить внимание, что в случае предопределенных строковых литералов (таких как const char *) константное значение в языке
Си
(и
Си++
)
обычно
неперезаписываемо, так как оно может храниться в
сегменте
памяти с
запретом
записи.
В дополнение к этому как
const
может быть объявлена функция-член (нестатическая). В этом случае указатель
this
внутри такой функции будет иметь тип
object_type const * const
вместо
object_type * const
. Это означает, что неконстантные по отношению к этому объекту функции изнутри такой функции вызваны быть не могут, также не могут быть модифицированы
поля класса
. В C++ поле класса может быть объявлено как
mutable
(изменяемое), что означает, что это ограничение к нему не относится. В некоторых случаях это может быть полезно, например, при
кешировании
,
подсчёте ссылок
и синхронизации данных. В этих случаях логический смысл (состояние) объекта неизменяем, но объект физически неконстантен, так как его поразрядное представление может измениться.
В C, C++, и D все типы данных, включая те, которые определены пользователем, могут быть объявлены
const
, и «
const
-овая правильность» предполагает, что все переменные или объекты должны быть объявлены таковыми, если их не нужно модифицировать. Такое предусмотрительное использование
const
делает значения переменных "простыми для понимания, отслеживания, и обдумывания"
, таким образом, читаемость и понятность увеличиваются и делают работу в командах и поддержку кода проще, потому что это предоставляет информацию о надлежащем использовании их значений. Это может помочь
компилятору
так же, как и разработчику при размышлениях над кодом. Это также может позволить
оптимизирующему компилятору
генерировать более эффективный код
.
Для простых типов данных (неуказателей) применение квалификатора
const
очевидно. Его можно указывать с любой стороны типа по историческим причинам (
const char foo = 'a';
эквивалентно
char const foo = 'a';
). В некоторых реализациях использование
const
с двух сторон типа (например,
const char const
) генерирует предупреждение, но не ошибку.
Для указателей и ссылок результирующий эффект
const
более запутан: и сам указатель, и значение, на которое он указывает, или оба могут быть объявлены как
const
. Более того, синтаксис также может сбивать с толку.
Указатель может быть объявлен
const
-указателем на перезаписываемое значение (
type * const x;
), или перезаписываемым указателем на
const
-значение (
type const * x; //или: const type * x;
), или
const
-указателем на
const
-значение (
type const * const x;
).
const
-указателю нельзя переприсвоить ссылку на другой объект с первоначального, но он (указатель) может быть использован для изменения значения, на которое он указывает (такое значение называется «
значение по указателю
» (
англ.
pointee
)). Таким образом, синтаксис ссылочных переменных — альтернативный синтакс
const
-указателей. С другой стороны, указателю на
const
-объект можно переприсвоить ссылку для указания на другую область памяти (которая должна содержать объект того же или приводимого типа), но его нельзя будет использовать, чтобы менять значения в памяти, на которые он указывает. Также может быть определен
const
-указатель на
const
-объект, который нельзя использовать, чтобы изменять значение по нему, и которому нельзя переприсвоить ссылку на другой объект.
Эти тонкости иллюстрирует следующий код:
void Foo (
int * ptr,
int const * ptrToConst,
int * const constPtr,
int const * const constPtrToConst)
{
*ptr = 0; //Порядок: меняет данные по указателю.
ptr = NULL; //Порядок: меняет указатель.
*ptrToConst = 0; //Ошибка! Нельзя менять данные по указателю.
ptrToConst = NULL; //Порядок: меняет указатель.
*constPtr = 0; //Порядок: меняет данные по указателю.
constPtr = NULL; //Ошибка! Нельзя менять указатель.
*constPtrToConst = 0; //Ошибка! Нельзя менять данные по указателю.
constPtrToConst = NULL; //Ошибка! Нельзя менять указатель.
}
В соответствие с обычными соглашениями языка Си об объявлениях последние указываются за предполагаемым использованием, а звёздочка у указателя к нему ставится вплотную, указывая на разыменование. Например, в объявлении
int *ptr
разыменованная форма
*ptr
является целым (
int
), а ссылочная форма
ptr
— указателем на целое. Таким образом,
const
модифицирует
имя переменной справа от себя
.
Соглашение в языке Си++ – наоборот, связывать
*
с типом (т. е.
int* ptr
) и читать, что
const
модифицирует
тип слева от себя
. Поэтому
int const * ptrToConst
можно прочитать или как «
*ptrToConst
– это
int const
» (значение по указателю неизменяемо), или как «
ptrToConst
– это
int const *
» (указатель на неизменяемое целое значение).
Таким образом:
int *ptr; //"*ptr" -- целое значение.
int const *ptrToConst; //"*ptrToConst" -- константа ("int" -- целая).
int * const constPtr; //"constPtr" -- константа ("int *" -- указатель на целое).
int const * const constPtrToConst; //"constPtrToConst" -- константа (указатель),
//как и "*constPtrToConst" (значение).
const
is part of the outermost derived type in a declaration; pointers complicate discussion.