константа (компьютерное программирование)
В некоторых языках программирования const — это квалификатор типа ( ключевое слово, применяемое к типу данных ), который указывает, что данные доступны только для чтения. Хотя это можно использовать для объявления констант , const в семействе языков C отличается от аналогичных конструкций в других языках тем, что является частью type и , таким образом, имеет сложное поведение в сочетании с указателями , ссылками, составными типами данных и проверкой типов . В других языках данные не хранятся в одной ячейке памяти , а копируются во время компиляции для каждого использования. [1] Языки, которые его используют, включают C , C++ , D , JavaScript , Julia и Rust .
Введение
[ редактировать ]в объекта объявлении При применении [а] это указывает на то, что объект является константой : его значение нельзя изменить, в отличие от переменной . Это основное использование — объявление констант — имеет параллели во многих других языках.
Однако, в отличие от других языков, в семье языков C const
является частью типа , а не частью объекта . Например, в С, int const x = 1;
объявляет объект x
из int const
тип – const
является частью типа, как если бы он был проанализирован "(int const) x" – в то время как в Ada , X : constant INTEGER := 1_
объявляет константу (разновидность объекта) X
из INTEGER
тип: constant
является частью объекта , но не частью типа .
Это имеет два тонких результата. Во-первых, const
можно применить к деталям более сложного типа – например, int const * const x;
объявляет постоянный указатель на постоянное целое число, а int const * x;
объявляет указатель переменной на постоянное целое число и int * const x;
объявляет постоянный указатель на целочисленную переменную. Во-вторых, потому что const
является частью типа, оно должно совпадать в рамках проверки типов. Например, следующий код недействителен:
void f(int& x);
// ...
int const i;
f(i);
потому что аргумент f
должно быть целочисленной переменной , но i
является постоянным целым числом. Это соответствие является формой корректности программы и известно как const-корректность . Это позволяет использовать форму программирования по контракту , где функции указывают в своей сигнатуре типа, изменяют ли они свои аргументы или нет, и можно ли их возвращаемое значение изменить или нет. Эта проверка типов в первую очередь представляет интерес для указателей и ссылок (не для базовых типов значений, таких как целые числа), но также для составных типов данных или шаблонных типов, таких как контейнеры . Оно скрыто тем, что const
часто может быть опущен из-за приведения типов (неявное преобразование типов ) и того, что C является вызовом по значению (C++ и D — это либо вызов по значению, либо вызов по ссылке).
Последствия
[ редактировать ]Идея константности не означает, что переменная, хранящаяся в памяти компьютера, не подлежит записи. Скорее, const
-ness — это конструкция времени компиляции , которая указывает, что программисту следует делать, а не обязательно, что он может сделать. Однако обратите внимание, что в случае предопределенных данных (таких как char const *
строковые литералы ), C const
часто невозможно записать.
Отличие от констант
[ редактировать ]Хотя константа не меняет своего значения во время работы программы, объявленный объект const
действительно может изменить свое значение во время работы программы. Типичным примером являются регистры только для чтения во встроенных системах, такие как текущее состояние цифрового входа. Регистры данных для цифровых входов часто объявляются как const
и volatile
. Содержимое этих регистров может меняться без каких-либо действий со стороны программы ( volatile
), но было бы неправильно, если бы программа попыталась записать в них запись ( const
).
Другое использование
[ редактировать ]Кроме того, (нестатическая) функция-член может быть объявлена как const
. В этом случае this
указатель внутри такой функции имеет тип object_type const *
а не просто типа object_type *
. [2] Это означает, что неконстантные функции для этого объекта не могут быть вызваны изнутри такой функции, а переменные-члены не могут быть изменены. В C++ переменная-член может быть объявлена как mutable
, что указывает на то, что данное ограничение на него не распространяется. В некоторых случаях это может быть полезно, например при кэшировании , подсчете ссылок и синхронизации данных . В этих случаях логическое значение (состояние) объекта не меняется, но объект не является физически постоянным, поскольку его побитовое представление может меняться.
Синтаксис
[ редактировать ]В C, C++ и D могут быть объявлены все типы данных, включая те, которые определены пользователем. const
, а const-correctness требует, чтобы все переменные или объекты были объявлены как таковые, если их не нужно изменять. Столь активное использование const
делает ценности «более понятными, отслеживаемыми и обоснованными», [3] и, таким образом, это повышает читаемость и понятность кода, а также упрощает работу в группах и поддержку кода, поскольку передает информацию о предполагаемом использовании значения. Это может помочь как компилятору , так и разработчику при обсуждении кода. Это также может позволить оптимизирующему компилятору генерировать более эффективный код. [4]
Простые типы данных
[ редактировать ]Для простых типов данных без указателей применение const
квалификатор простой. Он может идти по обе стороны от некоторых типов по историческим причинам (например, const char foo = 'a';
эквивалентно char const foo = 'a';
). В некоторых реализациях использование const
дважды (например, const char const
или char const const
) генерирует предупреждение, но не ошибку.
Указатели и ссылки
[ редактировать ]Для типов указателей и ссылок значение const
сложнее: либо сам указатель, либо значение, на которое он указывает, либо и то, и другое. const
. Кроме того, синтаксис может сбивать с толку. Указатель может быть объявлен как const
указатель на записываемое значение или записываемый указатель на const
ценность, или const
указатель на const
ценить. А const
Указатель нельзя переназначить, чтобы он указывал на объект, отличный от того, который ему изначально назначен, но его можно использовать для изменения значения, на которое он указывает (называемого указателем ) . [5] [6] [7] [8] [9] Ссылочные переменные в C++ представляют собой альтернативный синтаксис для const
указатели. Указатель на const
объект, с другой стороны, можно переназначить, чтобы он указывал на другую ячейку памяти (которая должна быть объектом того же типа или конвертируемого типа), но его нельзя использовать для изменения памяти, на которую он указывает. А const
указатель на const
объект также может быть объявлен и не может ни использоваться для изменения указателя, ни переназначаться для указания на другой объект. Следующий код иллюстрирует эти тонкости:
void Foo( int * ptr,
int const * ptrToConst,
int * const constPtr,
int const * const constPtrToConst )
{
*ptr = 0; // OK: modifies the pointed to data
ptr = NULL; // OK: modifies the pointer
*ptrToConst = 0; // Error! Cannot modify the pointed to data
ptrToConst = NULL; // OK: modifies the pointer
*constPtr = 0; // OK: modifies the pointed to data
constPtr = NULL; // Error! Cannot modify the pointer
*constPtrToConst = 0; // Error! Cannot modify the pointed to data
constPtrToConst = NULL; // Error! Cannot modify the pointer
}
Соглашение C
[ редактировать ]Следуя обычному соглашению C об объявлениях, объявление следует за использованием, а *
в указателе пишется на указателе, обозначающем разыменование . Например, в декларации int *ptr
, разыменованная форма *ptr
это int
, а справочная форма ptr
является указателем на int
. Таким образом const
изменяет имя справа. Вместо этого соглашение C++ заключается в том, чтобы связать *
с типом, как в int* ptr
и прочитайте const
как изменение типа слева. int const * ptrToConst
таким образом, можно прочитать как « *ptrToConst
это int const
" (значение постоянное) или " ptrToConst
это int const *
" (указатель является указателем на постоянное целое число). Таким образом:
int *ptr; // *ptr is an int value
int const *ptrToConst; // *ptrToConst is a constant (int: integer value)
int * const constPtr; // constPtr is a constant (int *: integer pointer)
int const * const constPtrToConst; // constPtrToConst is a constant pointer and points
// to a constant value
Соглашение С++
[ редактировать ]Следуя соглашению C++, согласно которому анализируется тип, а не значение, существует практическое правило : читать объявление справа налево. Таким образом, все, что находится слева от звезды, может быть идентифицировано как заостренный тип, а все, что справа от звезды, — это свойства указателя. Например, в нашем примере выше, int const *
может быть прочитан как записываемый указатель, который ссылается на недоступное для записи целое число, и int * const
может быть прочитан как недоступный для записи указатель, который ссылается на записываемое целое число.
Более общее правило, которое поможет вам понять сложные объявления и определения, работает следующим образом:
- найдите идентификатор, объявление которого вы хотите понять
- читайте как можно дальше вправо (т. е. до конца объявления или до следующей закрывающей скобки, в зависимости от того, что наступит раньше)
- вернитесь туда, где вы начали, и прочитайте назад влево (т. е. до начала объявления или до открывающей скобки, соответствующей закрывающей скобке, найденной на предыдущем шаге)
- когда вы дойдете до начала объявления, все готово. Если нет, перейдите к шагу 2 за закрывающей скобкой, которая была сопоставлена последней.
Вот пример:
Часть выражения | double (**const (*fun(int))(double))[10]
| Значение (читаем вниз) |
---|---|---|
Идентификатор | fun
| fun - это... |
Читать направо | (int))
| функция, ожидающая int... |
Найдите совпадение ( | (*
| возврат указателя на... |
Продолжайте двигаться правильно | (double))
| функция, ожидающая двойного значения... |
Найдите совпадение ( | (**const
| возвращение постоянного указателя на указатель на... |
Продолжайте двигаться правильно | [10]
| блоки по 10... |
Читать слева | double
| удваивается. |
При чтении налево важно читать элементы справа налево. Итак, int const *
становится указателем на const int , а не константным указателем на int .
В некоторых случаях C/C++ позволяет const
ключевое слово, которое будет размещено слева от типа. Вот несколько примеров:
const int *ptrToConst; //identical to: int const *ptrToConst,
const int *const constPtrToConst; //identical to: int const *const constPtrToConst
Хотя C/C++ допускает такие определения (которые близко соответствуют английскому языку при чтении определений слева направо), компилятор все равно читает определения в соответствии с вышеупомянутой процедурой: справа налево. Но положив const
прежде чем то, что должно быть постоянным, быстро приведет к несоответствию между тем, что вы собираетесь написать, и тем, что компилятор решит, что вы написали. Рассмотрим указатели на указатели:
int **ptr; // a pointer to a pointer to ints
int const **ptr // a pointer to a pointer to constant int value
// (not a pointer to a constant pointer to ints)
int *const *ptr // a pointer to a const pointer to int values
// (not a constant pointer to a pointer to ints)
int **const ptr // a constant pointer to pointers to ints
// (ptr, the identifier, being const makes no sense)
int const **const ptr // a constant pointer to pointers to constant int values
И последнее замечание относительно определений указателей: всегда пишите символ указателя (*) как можно правее. Присоединить символ указателя к типу сложно, поскольку он явно предполагает тип указателя, а это не так. Вот несколько примеров:
int* a; /* write: */ int *a; // a is a pointer to an int
int* a, b; // CONFUSING
/* write: */ int *a, b; // a is a pointer to an int,
// but b is a mere int
int* a, *b; // UGLY: both a and b are pointers to ints
/* write: */ int *a, *b;
Во избежание этой проблемы в FAQ Бьёрна Страуструпа при использовании соглашения C++ рекомендуется объявлять только одну переменную в строке. [10]
Те же соображения применимы к определению ссылок и ссылок rvalue:
int var = 22;
int const &refToConst = var; // OK
int const& ref2 = var, ref3 = var; // CONFUSING:
// ref2 is a reference, but ref3 isn't:
// ref3 is a constant int initialized with
// var's value
int &const constRef = var; // ERROR: as references can't change anyway.
// C++:
int&& rref = int(5), value = 10; // CONFUSING:
// rref is an rvalue reference, but value is
// a mere int.
/* write: */ int &&rref = int(5), value = 10;
Более сложные объявления встречаются при использовании многомерных массивов и ссылок (или указателей) на указатели. Хотя иногда утверждают [ ВОЗ? ] Поскольку такие объявления сбивают с толку и подвержены ошибкам, и поэтому их следует избегать или заменять структурами более высокого уровня, процедуру, описанную в начале этого раздела, всегда можно использовать, не внося двусмысленности или путаницы.
Параметры и переменные
[ редактировать ]const
может быть объявлен как для параметров функции, так и для переменных ( статических или автоматических, включая глобальные или локальные). Интерпретация варьируется в зависимости от использования. А const
статическая переменная (глобальная переменная или статическая локальная переменная) является константой и может использоваться для таких данных, как математические константы, такие как double const PI = 3.14159
– реально более длинные или общие параметры времени компиляции. А const
автоматическая переменная (нестатическая локальная переменная) означает, что происходит одно присвоение , хотя каждый раз может использоваться другое значение, например int const x_squared = x * x
. А const
параметр при передаче по ссылке означает, что указанное значение не изменяется – оно является частью контракта – в то время как const
параметр при передаче по значению (или сам указатель при передаче по ссылке) ничего не добавляет к интерфейсу (поскольку значение было скопировано), но указывает, что внутри функция не изменяет локальную копию параметр (это одно присвоение). По этой причине некоторые предпочитают использовать const
в параметрах только для передачи по ссылке, где он меняет контракт, но не для передачи по значению, где он предоставляет реализацию.
С++
[ редактировать ]Методы
[ редактировать ]Чтобы воспользоваться преимуществами подхода проектирования по контракту для определяемых пользователем типов (структур и классов), которые могут иметь как методы, так и данные-члены, программист может помечать методы экземпляра как const
если они не изменяют элементы данных объекта.
Применение const
Таким образом, квалификатор экземпляра метода является важной функцией для const-корректности и недоступен во многих других объектно-ориентированных языках, таких как Java и C# , а также в Microsoft или C++/CLI Managed Extensions for C++ .
Пока const
методы могут вызываться const
и не- const
объекты одинаковые, не const
методы могут быть вызваны только не- const
объекты.
const
модификатор метода экземпляра применяется к объекту, на который указывает " this
" указатель, который является неявным аргументом, передаваемым всем методам экземпляра.
Таким образом, имея const
методы — это способ применить константную корректность к неявным " this
"аргумент-указатель, как и другие аргументы.
Этот пример иллюстрирует:
class C
{
int i;
public:
int Get() const // Note the "const" tag
{ return i; }
void Set(int j) // Note the lack of "const"
{ i = j; }
};
void Foo(C& nonConstC, C const& constC)
{
int y = nonConstC.Get(); // Ok
int x = constC.Get(); // Ok: Get() is const
nonConstC.Set(10); // Ok: nonConstC is modifiable
constC.Set(10); // Error! Set() is a non-const method and constC is a const-qualified object
}
В приведенном выше коде неявное " this
"указатель на Set()
имеет тип " C *const
"; тогда как " this
"указатель на Get()
имеет тип " C const *const
", что указывает на то, что метод не может изменить свой объект через " this
"указатель.
Часто программист предоставляет как const
и не- const
метод с тем же именем (но, возможно, с совершенно разным использованием) в классе, подходящий для обоих типов вызывающих объектов. Учитывать:
class MyArray
{
int data[100];
public:
int & Get(int i) { return data[i]; }
int const & Get(int i) const { return data[i]; }
};
void Foo( MyArray & array, MyArray const & constArray )
{
// Get a reference to an array element
// and modify its referenced value.
array.Get( 5 ) = 42; // OK! (Calls: int & MyArray::Get(int))
constArray.Get( 5 ) = 42; // Error! (Calls: int const & MyArray::Get(int) const)
}
The const
-ness вызывающего объекта определяет, какая версия MyArray::Get()
будет вызван и, следовательно, будет ли вызывающему объекту предоставлена ссылка, с помощью которой он может манипулировать или только наблюдать за частными данными в объекте.
Технически эти два метода имеют разные сигнатуры, поскольку их " this
" Указатели имеют разные типы, что позволяет компилятору выбрать правильный. (Возврат const
ссылка на int
, вместо того, чтобы просто возвращать int
по значению, во втором методе это может быть излишним, но для произвольных типов можно использовать тот же метод, что и в стандартной библиотеке шаблонов .)
Лазейки в константной корректности
[ редактировать ]В C и C++ есть несколько лазеек для чистой константной корректности. Они существуют в первую очередь для совместимости с существующим кодом.
Первый, применимый только к C++, — это использование const_cast
, что позволяет программисту удалить const
квалификатор, делающий любой объект модифицируемым.
Необходимость удаления квалификатора возникает при использовании существующего кода и библиотек, которые нельзя изменить, но которые не являются константно-корректными. Например, рассмотрим этот код:
// Prototype for a function which we cannot change but which
// we know does not modify the pointee passed in.
void LibraryFunc(int* ptr, int size);
void CallLibraryFunc(int const * ptr, int size)
{
LibraryFunc(ptr, size); // Error! Drops const qualifier
int* nonConstPtr = const_cast<int*>(ptr); // Strip qualifier
LibraryFunc(nonConstPtr, size); // OK
}
Однако любая попытка изменить объект, который сам объявлен const
с помощью приведения const приводит к неопределенному поведению в соответствии со стандартом ISO C++.
В приведенном выше примере, если ptr
ссылается на глобальную, локальную переменную или переменную-член, объявленную как const
или объект, выделенный в куче через new int const
, код корректен только в том случае, если LibraryFunc
действительно не изменяет значение, на которое указывает ptr
.
Языку C нужна лазейка, потому что существует определенная ситуация. Переменным со статическим сроком хранения разрешено определять начальное значение. Однако инициализатор может использовать только константы, такие как строковые константы и другие литералы, и ему не разрешено использовать непостоянные элементы, такие как имена переменных, независимо от того, объявлены ли элементы инициализатора. const
или нет, или объявляется ли статическая переменная продолжительности const
или нет. Существует непереносимый способ инициализации const
переменная, имеющая статическую продолжительность хранения. Тщательно создав приведение типа в левой части более позднего задания, const
в переменную можно записать, эффективно удаляя const
атрибут и «инициализировать» его непостоянными элементами, такими как другие const
переменные и тому подобное. Запись в const
переменная таким образом может работать по назначению, но вызывает неопределенное поведение и серьезно противоречит константной корректности:
size_t const bufferSize = 8*1024;
size_t const userTextBufferSize; //initial value depends on const bufferSize, can't be initialized here
...
int setupUserTextBox(textBox_t *defaultTextBoxType, rect_t *defaultTextBoxLocation)
{
*(size_t*)&userTextBufferSize = bufferSize - sizeof(struct textBoxControls); // warning: might work, but not guaranteed by C
...
}
Еще одна лазейка [11] применимо как к C, так и к C++. В частности, языки требуют, чтобы указатели и ссылки на члены были «поверхностными» по отношению к const
-ность их владельцев – то есть вмещающий объект, который const
есть все const
членов, за исключением того, что члены-поинты (и рецензенты) по-прежнему изменчивы. Для иллюстрации рассмотрим следующий код C++:
struct S
{
int val;
int *ptr;
};
void Foo(S const & s)
{
int i = 42;
s.val = i; // Error: s is const, so val is a const int
s.ptr = &i; // Error: s is const, so ptr is a const pointer to int
*s.ptr = i; // OK: the data pointed to by ptr is always mutable,
// even though this is sometimes not desirable
}
Хотя объект s
перешел к Foo()
является константой, что делает все ее члены постоянными, а указатель доступен через s.ptr
все еще поддается модификации, хотя это может быть нежелательно с точки зрения const
-правильность, потому что s
может единолично владеть пуантом.
По этой причине Мейерс утверждает, что указатели и ссылки на члены по умолчанию должны быть «глубокими». const
-ность, которая может быть преодолена mutable
квалификатор, когда указатель не принадлежит контейнеру, но эта стратегия создаст проблемы совместимости с существующим кодом.
Таким образом, по историческим причинам [ нужна ссылка ] , эта лазейка остается открытой в C и C++.
Последнюю лазейку можно закрыть, используя класс, скрывающий указатель за const
-правильный интерфейс, но такие классы либо не поддерживают обычную семантику копирования из const
объект (подразумевается, что содержащий его класс также не может быть скопирован с помощью обычной семантики) или допускать другие лазейки, разрешая удаление const
-ность посредством непреднамеренного или намеренного копирования.
Наконец, несколько функций стандартной библиотеки C нарушают const-корректность до C23 , поскольку они принимают const
указатель на строку символов и вернуть не- const
указатель на часть той же строки. strstr
и strchr
входят в число этих функций.
Некоторые реализации стандартной библиотеки C++, например Microsoft. [12] попробуйте закрыть эту лазейку, предоставив две перегруженные версии некоторых функций: " const
"версия и "не- const
"Версия.
Проблемы
[ редактировать ]Этот раздел нуждается в расширении . Вы можете помочь, добавив к нему . ( ноябрь 2014 г. ) |
Использование системы типов для выражения постоянства приводит к различным сложностям и проблемам и, соответственно, подвергается критике и не принимается за пределами узкого семейства C, состоящего из C, C++ и D. Java и C#, на которые сильно влияют C и C++, оба явно отвергнуты const
квалификаторы типа -style, вместо того, чтобы выражать постоянство с помощью ключевых слов, которые применяются к идентификатору ( final
на Яве, const
и readonly
в С#). Даже в C и C++ использование const
значительно различается: некоторые проекты и организации используют его постоянно, а другие избегают.
strchr
проблема
[ редактировать ] The const
Квалификатор типа вызывает трудности, когда логика функции не зависит от того, является ли ее ввод константой или нет, но возвращает значение, которое должно быть того же квалифицированного типа, что и ввод. Другими словами, для этих функций, если входные данные являются константами (с константами), возвращаемое значение также должно быть, но если входные данные являются переменными (не const
-qualified), возвращаемое значение также должно быть. Поскольку сигнатуры типов этих функций различаются, для этого требуются две функции (или, возможно, больше, в случае нескольких входных данных) с одинаковой логикой — форма универсального программирования .
Эта проблема возникает даже для простых функций стандартной библиотеки C, особенно strchr
; это наблюдение Ричи приписывает Тому Пламу в середине 1980-х годов. [13] strchr
функция находит символ в строке; формально он возвращает указатель на первое появление символа c
в строке s
, а в классическом C (K&R C) его прототипом является:
char *strchr(char *s, int c);
The strchr
функция не изменяет входную строку, но возвращаемое значение часто используется вызывающей стороной для изменения строки, например:
if (p = strchr(q, '/'))
*p = ' ';
Таким образом, с одной стороны, входная строка может быть const
(поскольку она не изменяется функцией), и если входная строка const
возвращаемое значение также должно быть таким же – проще всего потому, что оно может возвращать именно указатель ввода, если первый символ совпадает – но, с другой стороны, возвращаемое значение не должно быть const
если исходная строка не была const
, поскольку вызывающая сторона может захотеть использовать указатель для изменения исходной строки.
В C++ это делается посредством перегрузки функций , обычно реализуемой через шаблон , в результате чего создаются две функции, так что возвращаемое значение имеет одинаковое значение. const
-квалифицированный тип в качестве входных данных: [б]
char* strchr(char* s, int c);
char const* strchr(char const* s, int c);
Они, в свою очередь, могут быть определены с помощью шаблона:
template <T>
T* strchr(T* s, int c) { ... }
В D это осуществляется через inout
Ключевое слово, которое действует как подстановочный знак для константы, неизменяемости или неквалифицированной (переменной), что дает: [14] [с]
inout(char)* strchr(inout(char)* s, int c);
Однако в C ни один из этих вариантов невозможен, поскольку C не имеет перегрузки функций, и вместо этого это решается с помощью одной функции, где входные данные постоянны, а выходные данные доступны для записи:
char *strchr(char const *s, int c);
Это позволяет использовать идиоматический код C, но удаляет квалификатор const, если входные данные действительно были квалифицированы как const, что нарушает безопасность типов. Это решение было предложено Ритчи и впоследствии принято. Это различие является одним из недостатков совместимости C и C++ .
Начиная с C23 , эта проблема решается с использованием универсальных функций. strchr
и другие функции, затронутые этой проблемой, вернут const
указатель, если он был им передан, и неквалифицированный указатель, если им был передан неквалифицированный указатель. [15]
Д
[ редактировать ]В версии 2 языка программирования D существуют два ключевых слова, относящиеся к const. [16] immutable
Ключевое слово обозначает данные, которые не могут быть изменены посредством какой-либо ссылки.
const
Ключевое слово обозначает неизменяемое представление изменяемых данных.
В отличие от С++ const
, Д const
и immutable
являются «глубокими» или транзитивными , и все, что достижимо через const
или immutable
объект const
или immutable
соответственно.
Пример константы и неизменности в D
int[] foo = new int[5]; // foo is mutable.
const int[] bar = foo; // bar is a const view of mutable data.
immutable int[] baz = foo; // Error: all views of immutable data must be immutable.
immutable int[] nums = new immutable(int)[5]; // No mutable reference to nums may be created.
const int[] constNums = nums; // Works. immutable is implicitly convertible to const.
int[] mutableNums = nums; // Error: Cannot create a mutable view of immutable data.
Пример транзитивной или глубокой константы в D
class Foo {
Foo next;
int num;
}
immutable Foo foo = new immutable(Foo);
foo.next.num = 5; // Won't compile. foo.next is of type immutable(Foo).
// foo.next.num is of type immutable(int).
История
[ редактировать ]const
был представлен Бьярном Страуструпом в C with Classes , предшественнике C++ , в 1981 году и первоначально назывался readonly
. [17] [18] Что касается мотивации, Страуструп пишет: [18]
- «Он выполнял две функции: как способ определения символической константы, которая подчиняется правилам области видимости и типа (то есть без использования макроса), и как способ считать объект в памяти неизменяемым».
Первое использование в качестве ограниченной и типизированной альтернативы макросам было аналогично выполнено для функциональных макросов через метод inline
ключевое слово. Постоянные указатели и * const
обозначения были предложены Деннисом Ритчи и приняты. [18]
const
затем был принят в C как часть стандартизации и появляется в C89 (и последующих версиях) вместе с другим квалификатором типа, volatile
. [19] Еще один квалификатор, noalias
, было предложено на заседании комитета X3J11 в декабре 1987 года, но было отклонено; ее цель была в конечном итоге достигнута restrict
ключевое слово в C99 . Ричи не очень поддержал эти дополнения, утверждая, что они «не несут своего веса», но в конечном итоге не выступал за их удаление из стандарта. [20]
D впоследствии унаследовал const
из C++, где он известен как конструктор типа (а не квалификатор типа ), и добавлены еще два конструктора типа, immutable
и inout
, для обработки связанных случаев использования. [д]
Другие языки
[ редактировать ]Другие языки не следуют C/C++ в части константности типа, хотя они часто имеют внешне похожие конструкции и могут использовать const
ключевое слово. Обычно это используется только для констант (постоянных объектов).
В C# есть const
Ключевое слово, но с радикально другой и более простой семантикой: оно означает константу времени компиляции и не является частью типа.
У Нима есть const
Ключевое слово аналогично ключевому слову C#: оно также объявляет константу времени компиляции, а не является частью типа. Однако в Nim константу можно объявить из любого выражения, которое можно вычислить во время компиляции. [21] В C# только встроенные типы C# могут быть объявлены как const
; определяемые пользователем типы, включая классы, структуры и массивы, не могут быть const
. [22]
Явы нет const
- вместо этого он имеет final
, который может применяться к объявлениям локальных «переменных» и применяется к идентификатору , а не к типу. Он имеет другое объектно-ориентированное использование членов объекта, что и является источником названия.
Спецификация языка Java касается const
как зарезервированное ключевое слово, т. е. такое, которое нельзя использовать в качестве идентификатора переменной, но которое не придает ему никакой семантики: это зарезервированное слово (оно не может использоваться в идентификаторах), но не ключевое слово (оно не имеет специального значения). Это ключевое слово было включено в качестве средства, позволяющего компиляторам Java обнаруживать и предупреждать о неправильном использовании ключевых слов C++. [23] Билет запроса на улучшение для реализации const
Корректность существует в процессе сообщества Java , но была закрыта в 2005 году на том основании, что ее невозможно реализовать с помощью обратной совместимости. [24]
Современная Ада 83 независимо имела понятие постоянного объекта и constant
ключевое слово, [25] [и] при этом входные параметры и параметры цикла неявно постоянны. Здесь constant
является свойством объекта, а не типа.
В JavaScript есть const
объявление, определяющее переменную в области блока , которую нельзя ни переназначить, ни переобъявить. Он определяет ссылку на переменную, доступную только для чтения, которую нельзя переопределить, но в некоторых ситуациях значение самой переменной потенциально может измениться, например, если переменная ссылается на объект и изменяется его свойство. [26]
См. также
[ редактировать ]Примечания
[ редактировать ]- ^ Формально, когда
const
является частью самого внешнего производного типа в объявлении; указатели усложняют обсуждение. - ^ Обратите внимание, что правила синтаксиса объявления указателей различаются в C и C++: в C
char *s
является стандартным, а в C++char* s
является стандартным. - ^ Идиоматический код D здесь будет использовать массив вместо указателя. [14]
- ^ D также представил
shared
конструктор типа, но это связано со случаями использованияvolatile
, нетconst
. - ^ В стандарте Ada это называется « зарезервированным словом »; см. эту статью для использования.
Ссылки
[ редактировать ]- ^ «Постоянные предметы – Справочник по ржавчине» . doc.rust-lang.org . Проверено 22 июня 2022 г.
- ^ "
this
указатель» . Черновик стандарта C++ . Проверено 30 марта 2020 г ..Тип
this
в функции-члене, тип которой имеет cv-qualifier-seq cv и чей классX
это «указатель на резюмеX
". - ^ Херб Саттер и Андрей Александреску (2005). Стандарты кодирования C++ . п. 30. Бостон: Эддисон Уэсли. ISBN 0-321-11358-6
- ^ «Почему аргумент kfree() является константным?» . lkml.org. 12 января 2013 г.
- ^ «5.1. Расширения, реализованные в GNU Fortran: 5.1.16 Указатели Cray». Компилятор GNU Fortran . 2006. Архивировано из оригинала 21 декабря 2022 г. Проверено 21 декабря 2022 г.
- ^ Фэйи, Марк Р.; Нэгл, Дэн (19 апреля 1999 г.). «Указатели Cray Fortran против указателей Fortran 90 и перенос с Cray C90 на SGI Origin2000» (PDF) . Виксбург, Массачусетс, США: Экспериментальная станция водных путей Инженерного корпуса армии США, Главный центр общих ресурсов. Архивировано (PDF) из оригинала 23 декабря 2022 г. Проверено 23 декабря 2022 г. (8 страниц)
- ^ «Приложение C: Особенности и различия Fortran 90 > Возможности > Указатели Cray» . Руководство пользователя Фортрана . Корпорация Оракл . 2010. Архивировано из оригинала 21 сентября 2021 г. Проверено 23 декабря 2022 г.
- ^ «Приложение C: Особенности и различия Fortran 90 > Возможности > Указатели символов Cray» . Руководство пользователя Фортрана . Корпорация Оракл . 2010. Архивировано из оригинала 23 декабря 2022 г. Проверено 23 декабря 2022 г.
- ^ «Глава 4. Типы данных». Справочное руководство по языку Фортран, Том 1 . Том. 1. Silicon Graphics, Inc. 1999 [1993]. Номер документа: 007-3692-004. Архивировано из оригинала 23 декабря 2022 г. Проверено 23 декабря 2022 г. (Примечание. Взято из «РУКОВОДСТВА ПО FORTRAN 90» (1992, McGraw-Hill, Inc. ) Уолтера С. Брейнерда, Джин К. Адамс, Джин Т. Мартин, Брайана Т. Смита и Джерролда Л. Вагенера.)
- ^ «Страуструп: Часто задаваемые вопросы по стилю и технике C++» .
- ^ Скотт Мейерс (2005). Эффективный C++, третье издание . стр. 21–23. Бостон: Эддисон Уэсли. ISBN 978-0-321-33487-9
- ^ "strchr, wcschr, _mbschr (CRT)" . Msdn.microsoft.com . Проверено 23 ноября 2017 г.
- ^ «Деннис Ритчи: Почему мне не нравятся квалификаторы типа X3J11» .
- ^ Jump up to: а б Язык программирования D, Андрей Александреску , 8.8: Передача квалификатора от параметра к результату
- ^ «WG14-N3020: Функции стандартной библиотеки, сохраняющие квалификаторы» (PDF) . open-std.org . 13.06.2022. Архивировано (PDF) из оригинала 13 октября 2022 г.
- ^ «const(FAQ) – язык программирования D» . Digitalmars.com . Проверено 18 августа 2013 г.
- ^ Бьерн Страуструп , «Расширения концепции типа языка C», внутренний технический меморандум Bell Labs, 5 января 1981 г.
- ^ Jump up to: а б с Соперничество между братьями и сестрами: C и C++ , Бьёрн Страуструп , 2002, стр. 5
- ^ Деннис М. Ритчи , « Развитие языка C. Архивировано 15 июля 2012 г., archive.today », 2003 г.: «X3J11 также внес множество мелких дополнений и корректировок, например, квалификаторы типа const и voluty и немного другие правила продвижения по типу».
- ^ «Позвольте мне начать с того, что я не уверен, что даже квалификаторы до декабря («const» и «летучие») имеют свой вес; я подозреваю, что то, что они добавляют к стоимости изучения и использования языка, не является отплатил большей выразительностью. «Неустойчивость», в частности, является дополнением к эзотерическим приложениям и гораздо лучше выражается другими средствами. Его главное достоинство в том, что почти каждый может о нем забыть. «Const» одновременно более полезен и более навязчив; вы не можете не узнать об этом из-за его присутствия в интерфейсе библиотеки. Тем не менее, я не выступаю за отмену квалификаций хотя бы потому, что уже слишком поздно».
- ^ Руководство Nim: раздел Const
- ^ const (Справочник по C#)
- ^ Гослинг, Джеймс; Джой, Билл; Стил, Гай. «Спецификация языка Java, третье издание» .
- ^ «Идентификатор ошибки: JDK-4211070 Java должна поддерживать константные параметры (например, C++) для поддержки кода [ sic ]» . Bugs.sun.com . Проверено 4 ноября 2014 г.
- ^ 1815А [ мертвая ссылка ] , 3.2.1. Объявления объектов. Архивировано 20 октября 2014 г. в Wayback Machine :
«Объявленный объект является константой, если в объявлении объекта появляется зарезервированное слово константа; тогда объявление должно включать явную инициализацию. Значение константы не может быть изменено после инициализации. Формальные параметры режима в подпрограммах и записях, а также общие параметры формальные параметры режима in также являются константами; параметр цикла является константой внутри соответствующего цикла; подкомпонент или фрагмент константы является константой. - ^ «константа» . МДН . Проверено 31 октября 2017 г.
Внешние ссылки
[ редактировать ]- «Константная корректность» Херба Саттера
- «Постоянная оптимизация?» Херб Саттер
- Часто задаваемые вопросы по C++ Lite: корректность констант от Маршалла Клайна
- Раздел « Подстановка значений » из бесплатной электронной книги Мышление на C++» « Брюса Экеля
- «Здесь константа, там константа» Брайта Уолтера
- «Конст и инвариант» из спецификации языка программирования D, версия 2 (экспериментальная)