stringtranslate.com

const (компьютерное программирование)

В некоторых языках программирования const — это квалификатор типа ( ключевое слово, применяемое к типу данных ), который указывает, что данные доступны только для чтения. Хотя это может использоваться для объявления констант , const в семействе языков C отличается от аналогичных конструкций в других языках тем, что является частью типа и, таким образом, имеет сложное поведение при сочетании с указателями , ссылками, составными типами данных и проверкой типов . В других языках данные не находятся в одном месте памяти , а копируются во время компиляции для каждого использования. [1] Языки, которые его используют, включают C , C++ , D , JavaScript , Julia и Rust .

Введение

При применении в объявлении объекта [a] он указывает, что объект является константой : его значение не может быть изменено, в отличие от переменной . Это базовое использование — для объявления констант — имеет параллели во многих других языках.

Однако, в отличие от других языков, в семействе языков C constявляется частью типа , а не частью объекта . Например, в C объявляет объект типа – является частью типа, как если бы он был проанализирован как "(int const) x" – в то время как в Ada объявляет константу (вид объекта) типа : является частью объекта , но не частью типа .int const x = 1;xint constconstX : constant INTEGER := 1_XINTEGERconstant

Это имеет два тонких результата. Во-первых, 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-ность — это конструкция времени компиляции , которая указывает, что программист должен делать, а не обязательно то, что он может делать. Обратите внимание, однако, что в случае предопределенных данных (таких как char const * строковые литералы ) C constчасто не подлежит записи.

Отличие от констант

В то время как константа не изменяет своего значения во время работы программы, объявленный объект constдействительно может изменить свое значение во время работы программы. Типичным примером являются регистры только для чтения во встроенных системах, такие как текущее состояние цифрового входа. Регистры данных для цифровых входов часто объявляются как constи volatile. Содержимое этих регистров может изменяться без каких-либо действий со стороны программы ( volatile), но было бы неправильно для программы пытаться записывать в них ( const).

Другие применения

Кроме того, (нестатическая) функция-член может быть объявлена ​​как const. В этом случае thisуказатель внутри такой функции имеет тип object_type const *, а не просто тип object_type *. [2] Это означает, что неконстантные функции для этого объекта не могут быть вызваны изнутри такой функции, а переменные-члены не могут быть изменены. В C++ переменная-член может быть объявлена ​​как mutable, указывая, что это ограничение к ней не применяется. В некоторых случаях это может быть полезно, например, при кэшировании , подсчете ссылок и синхронизации данных . В этих случаях логическое значение (состояние) объекта не изменяется, но объект не является физически постоянным, поскольку его побитовое представление может измениться.

Синтаксис

В C, C++ и D все типы данных, включая те, которые определены пользователем, могут быть объявлены const, а const-корректность диктует, что все переменные или объекты должны быть объявлены как таковые, если только их не нужно изменять. Такое упреждающее использование 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не может быть переназначен для указания на другой объект, нежели тот, который ему изначально назначен, но его можно использовать для изменения значения, на которое он указывает (называемого pointee ) . [5] [6] [7] [8] [9] Ссылочные переменные в C++ являются альтернативным синтаксисом для constуказателей. Указатель на constобъект, с другой стороны, может быть переназначен для указания на другую область памяти (которая должна быть объектом того же типа или конвертируемого типа), но его нельзя использовать для изменения памяти, на которую он указывает. constУказатель на constобъект также может быть объявлен и не может ни использоваться для изменения apointee, ни переназначаться для указания на другой объект. Следующий код иллюстрирует эти тонкости:

void Foo ( int * ptr , int const * ptrToConst , int * const constPtr , int const * const constPtrToConst ) { * ptr = 0 ; // OK: изменяет указанные данные ptr = NULL ; // OK: изменяет указатель                           * ptrToConst = 0 ; // Ошибка! Невозможно изменить указанные данные ptrToConst = NULL ; // OK: изменяет указатель        * constPtr = 0 ; // OK: изменяет указанные данные constPtr = NULL ; // Ошибка! Невозможно изменить указатель        * constPtrToConst = 0 ; // Ошибка! Невозможно изменить указанные данные constPtrToConst = NULL ; // Ошибка! Невозможно изменить указатель }       

C-конвенция

Следуя обычному соглашению C для объявлений, объявление следует за использованием, и *в указателе записывается указатель, указывая на разыменование . Например, в объявлении int *ptrразыменованная форма *ptr— это int, в то время как ссылочная форма ptr— это указатель на int. Таким образом, constизменяет имя справа. Соглашение C++ вместо этого заключается в том, чтобы связать *с типом, как в int* ptr, и читать constкак изменяющий тип слева. int const * ptrToConstтаким образом, можно читать как « *ptrToConstis a int const» (значение является константой) или « ptrToConstis a int const *» (указатель является указателем на постоянное целое число). Таким образом:

int * ptr ; // *ptr — это значение типа int int const * ptrToConst ; // *ptrToConst — это константа (int: целое значение) int * const constPtr ; // constPtr — это константа (int *: указатель на целое число) int const * const constPtrToConst ; // constPtrToConst — это указатель-константа, указывающий // на постоянное значение               

Соглашение C++

Следуя соглашению C++ об анализе типа, а не значения, эмпирическое правило заключается в том, чтобы читать объявление справа налево. Таким образом, все, что слева от звезды, может быть идентифицировано как указанный тип, а все, что справа от звезды, является свойствами указателя. Например, в нашем примере выше int const *может быть прочитано как записываемый указатель, который ссылается на не записываемое целое число, и int * constможет быть прочитано как незаписываемый указатель, который ссылается на записываемое целое число.

Более общее правило, которое поможет вам понять сложные объявления и определения, работает следующим образом:

  1. найдите идентификатор, объявление которого вы хотите понять
  2. читать как можно дальше вправо (т.е. до конца объявления или до следующей закрывающей скобки, в зависимости от того, что наступит раньше)
  3. вернитесь к тому месту, где вы начали, и прочитайте в обратном порядке влево (т. е. до начала объявления или до открывающейся скобки, соответствующей закрывающейся скобке, найденной на предыдущем шаге)
  4. когда вы достигли начала объявления, вы закончили. Если нет, продолжайте с шага 2, после закрывающей скобки, которая была сопоставлена ​​последней.

Вот пример:

При чтении слева важно читать элементы справа налево. Таким образом, an int const *становится указателем на const int , а не const указателем на int .

В некоторых случаях C/C++ позволяет constразмещать ключевое слово слева от типа. Вот несколько примеров:

const int * ptrToConst ; //идентично: int const *ptrToConst, const int * const constPtrToConst ; //идентично: int const *const constPtrToConst       

Хотя C/C++ допускает такие определения (которые близко соответствуют английскому языку при чтении определений слева направо), компилятор все равно читает определения согласно вышеупомянутой процедуре: справа налево. Но размещение const перед тем, что должно быть константой, быстро вносит несоответствия между тем, что вы намереваетесь написать, и тем, что компилятор решает, что вы написали. Рассмотрим указатели на указатели:

int ** ptr ; // указатель на указатель на целые числа int const ** ptr // указатель на указатель на константное значение целого числа // (не указатель на константный указатель на целые числа) int * const * ptr // указатель на константный указатель на значения целых чисел // (не константный указатель на указатель на целые числа) int ** const ptr // константный указатель на указатели на целые числа // (ptr, идентификатор, будучи константой, не имеет смысла) int const ** const ptr // константный указатель на указатели на константные значения целых чисел                  

В качестве последнего замечания относительно определений указателей: всегда пишите символ указателя (*) как можно правее. Присоединение символа указателя к типу — сложная задача, поскольку он явно предполагает тип указателя, что не соответствует действительности. Вот несколько примеров:

int * a ; /* запись: */ int * a ; // a — указатель на int int * a , b ; // ЗАПУТАННОСТЬ /* запись: */ int * a , b ; // a — указатель на int, // но b — это просто int int * a , * b ; // УРОДЛИВОСТЬ: и a, и b — указатели на int /* запись: */ int * a , * b ;                     

В разделе часто задаваемых вопросов Бьярна Страуструпа рекомендуется объявлять только одну переменную на строку при использовании соглашения C++, чтобы избежать этой проблемы. [10]

Те же соображения применимы к определению ссылок и ссылок rvalue:

int var = 22 ; int const & refToConst = var ; // OK int const & ref2 = var , ref3 = var ; // ЗАПУТАННОСТЬ: // ref2 — это ссылка, а ref3 — нет: // ref3 — это константа int, инициализированная // значением var int & const constRef = var ; // ОШИБКА: так как ссылки в любом случае не могут изменяться.                        // C++: int && rref = int ( 5 ), value = 10 ; // ЗАПУТАННОСТЬ: // rref — это ссылка rvalue, но value — это // просто int. /* запись: */ int && rref = int ( 5 ), value = 10 ;                 

Более сложные объявления встречаются при использовании многомерных массивов и ссылок (или указателей) на указатели. Хотя иногда утверждается [ who? ], что такие объявления запутанны и подвержены ошибкам, и что поэтому их следует избегать или заменять структурами более высокого уровня, процедура, описанная в начале этого раздела, всегда может использоваться без внесения двусмысленностей или путаницы.

Параметры и переменные

constможет быть объявлен как в параметрах функции, так и в переменных ( статических или автоматических, включая глобальные или локальные). Интерпретация различается в зависимости от использования. constСтатическая переменная (глобальная переменная или статическая локальная переменная) является константой и может использоваться для таких данных, как математические константы, такие как double const PI = 3.14159– реалистично более длинные, или общие параметры времени компиляции. constАвтоматическая переменная (нестатическая локальная переменная) означает, что происходит единичное присваиваниеint const x_squared = x * x , хотя каждый раз может использоваться другое значение, например . constПараметр в передаче по ссылке означает, что указанное значение не изменяется – оно является частью контракта – в то время как constпараметр в передаче по значению (или сам указатель в передаче по ссылке) ничего не добавляет к интерфейсу (поскольку значение было скопировано), но указывает, что внутренне функция не изменяет локальную копию параметра (это единичное присваивание). По этой причине некоторые предпочитают использовать constпараметры только для передачи по ссылке, где он изменяет контракт, но не для передачи по значению, где он раскрывает реализацию.

С++

Методы

Чтобы воспользоваться преимуществами подхода проектирования по контракту для определяемых пользователем типов (структур и классов), которые могут иметь методы, а также данные-члены, программист может пометить методы экземпляра так, как constбудто они не изменяют данные-члены объекта. constТаким образом, применение квалификатора к методам экземпляра является существенной функцией для константной корректности и недоступно во многих других объектно-ориентированных языках, таких как Java и C# или в Microsoft C++/CLI или Managed Extensions для C++ . В то время как constметоды могут вызываться как объектами, так constи не- constобъектами, не- constметоды могут вызываться только не- constобъектами. constМодификатор метода экземпляра применяется к объекту, на который указывает thisуказатель " ", который является неявным аргументом, передаваемым всем методам экземпляра. Таким образом, наличие constметодов является способом применения константной корректности к неявному thisаргументу указателя " ", как и к другим аргументам.

Этот пример иллюстрирует:

class C { int i ; public : int Get () const // Обратите внимание на тег "const" { return i ; } void Set ( int j ) // Обратите внимание на отсутствие "const" { i = j ; } };                    void Foo ( C & nonConstC , C const & constC ) { int y = nonConstC . Get (); // Хорошо int x = constC . Get (); // Хорошо: Get() является константой                nonConstC . Set ( 10 ); // Ok: nonConstC можно изменять constC . Set ( 10 ); // Ошибка! Set() — неконстантный метод, а constC — константный объект }   

В приведенном выше коде неявный 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 ) { // Получить ссылку на элемент массива // и изменить его указанное значение.            array.Get ( 5 ) = 42 ; // ОК! (Вызовы: int & MyArray::Get(int)) constArray.Get ( 5 ) = 42 ; // Ошибка ! (Вызовы: int const & MyArray::Get(int) const ) }           

-ность constвызывающего объекта определяет, какая версия MyArray::Get()будет вызвана и, таким образом, будет ли вызывающему предоставлена ​​ссылка, с помощью которой он может манипулировать или только наблюдать за закрытыми данными в объекте. Технически эти два метода имеют разные сигнатуры, поскольку их thisуказатели " " имеют разные типы, что позволяет компилятору выбрать правильный. (Возврат constссылки на int, вместо простого возврата intпо значению, может быть излишним во втором методе, но тот же прием можно использовать для произвольных типов, как в Standard Template Library .)

Лазейки к константной корректности

В C и C++ есть несколько лазеек к чистой константной корректности. Они существуют в первую очередь для совместимости с существующим кодом.

Первый, который применим только к C++, — это использование const_cast, которое позволяет программисту удалить constквалификатор, сделав любой объект модифицируемым. Необходимость удаления квалификатора возникает при использовании существующего кода и библиотек, которые нельзя изменить, но которые не являются const-корректными. Например, рассмотрим этот код:

// Прототип для функции, которую мы не можем изменить, но которая, как мы знаем, // не изменяет переданный указатель. void LibraryFunc ( int * ptr , int size );    void CallLibraryFunc ( int const * ptr , int size ) { LibraryFunc ( ptr , size ); // Ошибка! Удаляет квалификатор const          int * nonConstPtr = const_cast < int *> ( ptr ); // Удалить квалификатор 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 ; //начальное значение зависит от const bufferSize, здесь его инициализировать нельзя       ...int setupUserTextBox ( textBox_t * defaultTextBoxType , rect_t * defaultTextBoxLocation ) { * ( size_t * ) & userTextBufferSize = bufferSize - sizeof ( struct textBoxControls ); // предупреждение: может работать, но не гарантируется C ... }            

Другая лазейка [11] применима как к C, так и к C++. В частности, языки диктуют, что указатели и ссылки на члены являются «поверхностными» по отношению к -ности constих владельцев – то есть, содержащий объект, который constимеет все constчлены, за исключением тех, что указатели членов (и рефери) остаются изменяемыми. Для иллюстрации рассмотрим этот код C++:

структура S { int val ; int * ptr ; };     void Foo ( S const & s ) { int i = 42 ; s . val = i ; // Ошибка: s является константой, поэтому val является константой int s . ptr = & i ; // Ошибка: s является константой, поэтому ptr является константным указателем на int * s . ptr = i ; // OK: данные, на которые указывает ptr, всегда изменяемы, // хотя иногда это нежелательно }                     

sХотя переданный объект Foo()является константой, что делает все его члены константами, указатель, доступный через него, s.ptrвсе еще может быть изменен, хотя это может быть нежелательно с точки зрения const-корректности, поскольку sможет единолично владеть указателем. По этой причине Мейерс утверждает, что значение по умолчанию для указателей и ссылок на члены должно быть "глубоким" const-ness, которое может быть переопределено квалификатором mutable, когда указатель не принадлежит контейнеру, но эта стратегия создаст проблемы совместимости с существующим кодом. Таким образом, по историческим причинам [ требуется цитата ] эта лазейка остается открытой в C и C++.

Последнюю лазейку можно закрыть, используя класс для сокрытия указателя за const-корректным интерфейсом, но такие классы либо не поддерживают обычную семантику копирования из constобъекта (подразумевая, что содержащий их класс также не может быть скопирован с помощью обычной семантики), либо допускают другие лазейки, разрешая удаление -ности constпосредством непреднамеренного или преднамеренного копирования.

Наконец, несколько функций в стандартной библиотеке C нарушают const-корректность до C23 , поскольку они принимают constуказатель на строку символов и возвращают не- constуказатель на часть той же строки. strstrи strchrвходят в число этих функций. Некоторые реализации стандартной библиотеки C++, такие как [12] от Microsoft, пытаются закрыть эту лазейку, предоставляя две перегруженные версии некоторых функций: constверсию " " и "не- const" версию.

Проблемы

Использование системы типов для выражения постоянства приводит к различным сложностям и проблемам, и соответственно подвергалось критике и не было принято за пределами узкого семейства C, состоящего из C, C++ и D. Java и C#, которые находятся под сильным влиянием C и C++, явно отвергли constквалификаторы типов в стиле -, вместо этого выражая постоянство ключевыми словами, которые применяются к идентификатору ( finalв Java constи readonlyв C#). Даже в C и C++ использование constзначительно различается, некоторые проекты и организации используют его последовательно, а другие избегают.

strchrпроблема

Квалификатор типа constвызывает трудности, когда логика функции не зависит от того, является ли ее вход константой или нет, но возвращает значение, которое должно быть того же квалифицированного типа, что и вход. Другими словами, для этих функций, если вход константа (квалифицирована const), возвращаемое значение должно быть таким же, но если вход переменный (не constквалифицирован -), возвращаемое значение должно быть таким же. Поскольку сигнатура типа этих функций различается, требуются две функции (или потенциально больше, в случае нескольких входов) с одинаковой логикой — форма обобщенного программирования .

Эта проблема возникает даже для простых функций в стандартной библиотеке C, в частности strchr; это наблюдение приписывается Ритчи Тому Пламу в середине 1980-х годов. [13] Функция strchrнаходит символ в строке; формально она возвращает указатель на первое вхождение символа cв строку s, а в классическом C (K&R C) ее прототипом является:

char * strchr ( char * s , int c );    

Функция strchrне изменяет входную строку, но возвращаемое значение часто используется вызывающей стороной для изменения строки, например:

если ( p = strchr ( q , '/' )) * p = ' ' ;       

Таким образом, с одной стороны, входная строка может быть const(поскольку она не изменяется функцией), и если входная строка является, constто возвращаемое значение также должно быть — в основном потому, что оно может вернуть именно указатель ввода, если первый символ совпадает, — но с другой стороны, возвращаемое значение не должно быть, constесли исходная строка не была const, поскольку вызывающая сторона может захотеть использовать указатель для изменения исходной строки.

В C++ это делается с помощью перегрузки функций , обычно реализуемой с помощью шаблона , что приводит к двум функциям, так что возвращаемое значение имеет тот же const-квалифицированный тип, что и входные данные: [b]

char * strchr ( char * s , int c ); char const * strchr ( char const * s , int c );          

Их, в свою очередь, можно определить с помощью шаблона:

шаблон < T > T * strchr ( T * s , int c ) { ... }        

В языке D это обрабатывается с помощью inoutключевого слова, которое действует как подстановочный знак для константы, неизменяемого или неквалифицированного (переменного), что дает: [14] [c]

inout ( char )* strchr ( inout ( char )* s , int c );    

Однако в языке C ни один из этих вариантов невозможен, поскольку в нем нет перегрузки функций, и вместо этого эта проблема решается с помощью одной функции, где входные данные являются константой, а выходные данные доступны для записи:

char * strchr ( char const * s , int c );     

Это позволяет использовать идиоматический код C, но удаляет квалификатор const, если входные данные на самом деле были квалифицированы const, нарушая безопасность типов. Это решение было предложено Ритчи и впоследствии принято. Это различие является одним из недостатков совместимости C и C++ .

Начиная с C23 , эта проблема решается с помощью универсальных функций. strchrА другие функции, затронутые этой проблемой, будут возвращать constуказатель, если им был передан указатель, и неквалифицированный указатель, если им был передан неквалифицированный указатель. [15]

Д

В версии 2 языка программирования D существуют два ключевых слова, относящихся к const. [16] Ключевое immutableслово обозначает данные, которые не могут быть изменены посредством какой-либо ссылки. Ключевое constслово обозначает неизменяемое представление изменяемых данных. В отличие от C++ const, D constи immutableявляются «глубокими» или транзитивными , и все, что достижимо посредством объекта constили, immutableявляется constили immutableсоответственно.

Пример const и immutable в D

int [] foo = new int [ 5 ]; // foo является изменяемыми. const int [] bar = foo ; // bar является константным представлением изменяемыми данными. immutable int [] baz = foo ; // Ошибка: все представления неизменяемых данных должны быть неизменяемыми.               immutable int [] nums = new immutable ( int )[ 5 ]; // Невозможно создать изменяемую ссылку на nums. const int [] constNums = nums ; // Работает. immutable неявно преобразуется в const. int [] mutableNums = nums ; // Ошибка: невозможно создать изменяемое представление неизменяемых данных.               

Пример транзитивной или глубокой константы в D

класс Foo { Foo следующий ; int num ; }      immutable Foo foo = new immutable ( Foo ); foo . next . num = 5 ; // Не скомпилируется. foo.next имеет тип immutable(Foo). // foo.next.num имеет тип 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, для обработки связанных вариантов использования. [d]

Другие языки

Другие языки не следуют C/C++ в том, что касается константности как части типа, хотя они часто имеют внешне похожие конструкции и могут использовать constключевое слово. Обычно это используется только для констант (константных объектов).

В C# есть constключевое слово, но с радикально иной и более простой семантикой: оно означает константу времени компиляции и не является частью типа.

Nim имеет constключевое слово, похожее на ключевое слово C#: оно также объявляет константу времени компиляции, а не формирует часть типа. Однако в Nim константа может быть объявлена ​​из любого выражения, которое может быть вычислено во время компиляции. [21] В C# только встроенные типы C# могут быть объявлены как const; определяемые пользователем типы, включая классы, структуры и массивы, не могут быть const. [22]

В Java нет const– вместо этого есть final, который может применяться к локальным объявлениям «переменных» и применяется к идентификатору , а не к типу. У него есть другое объектно-ориентированное использование для членов объекта, что и является источником названия.

Спецификация языка Java рассматривает constкак зарезервированное ключевое слово – то есть такое, которое не может использоваться как идентификатор переменной – но не назначает ему семантики: это зарезервированное слово (его нельзя использовать в идентификаторах), но не ключевое слово (у него нет специального значения). Ключевое слово было включено как средство для компиляторов Java обнаруживать и предупреждать о неправильном использовании ключевых слов C++. [23] Билет запроса на улучшение для реализации constкорректности существует в Java Community Process , но был закрыт в 2005 году на основании того, что его было невозможно реализовать обратно совместимым образом. [24]

Современная Ada 83 независимо имела понятие постоянного объекта и constantключевого слова, [25] [e] с входными параметрами и параметрами цикла, которые были неявно постоянными. Здесь constantявляется свойством объекта, а не типа.

В JavaScript есть constобъявление, которое определяет переменную с областью действия блока , которую нельзя ни переназначить, ни переобъявить. Оно определяет ссылку только для чтения на переменную, которую нельзя переопределить, но в некоторых ситуациях значение самой переменной может потенциально измениться, например, если переменная ссылается на объект, а его свойство изменено. [26]

Смотрите также

Примечания

  1. ^ Формально, когда constявляется частью самого внешнего производного типа в объявлении; указатели усложняют обсуждение.
  2. ^ Обратите внимание, что соглашения о синтаксисе объявления указателей различаются в языках C и C++: в C char *sэто стандарт, а в C++ char* sэто стандарт.
  3. ^ Идиоматический код D использовал бы здесь массив вместо указателя. [14]
  4. ^ D также ввел sharedконструктор типов, но он относится к вариантам использования volatile, а не const.
  5. ^ В стандарте Ada это называется « зарезервированным словом »; см. эту статью для ознакомления с использованием.

Ссылки

  1. ^ "Постоянные элементы – Справочник Rust". doc.rust-lang.org . Получено 2022-06-22 .
  2. ^ "Указатель this". Проект стандарта C++ . Получено 2020-03-30 . Тип в функции-члене, тип которой имеет cv-qualifier-seq cv и класс которой является "указатель на cv ".thisX X
  3. ^ Херб Саттер и Андрей Александреску (2005). Стандарты кодирования C++ . стр. 30. Бостон: Addison Wesley. ISBN 0-321-11358-6 
  4. ^ «Почему аргумент kfree() является константным?». lkml.org. 2013-01-12.
  5. ^ "5.1. Расширения, реализованные в GNU Fortran: 5.1.16 Указатели Cray". Компилятор GNU Fortran. 2006. Архивировано из оригинала 21.12.2022 . Получено 21.12.2022 .
  6. ^ Фэйи, Марк Р.; Нагл, Дэн (1999-04-19). "Указатели Cray Fortran против указателей Fortran 90 и портирование с Cray C90 на SGI Origin2000" (PDF) . Виксбург, Массачусетс, США: Экспериментальная станция инженерных войск США по водным путям, Главный центр общих ресурсов. Архивировано (PDF) из оригинала 2022-12-23 . Получено 2022-12-23 .(8 страниц)
  7. ^ "Приложение C: Возможности и различия Fortran 90 > Возможности > Указатели Cray". Руководство пользователя Fortran . Корпорация Oracle . 2010. Архивировано из оригинала 21.09.2021 . Получено 23.12.2022 .
  8. ^ "Приложение C: Возможности и различия Fortran 90 > Возможности > Указатели символов Cray". Руководство пользователя Fortran . Корпорация Oracle . 2010. Архивировано из оригинала 2022-12-23 . Получено 2022-12-23 .
  9. ^ "Глава 4. Типы данных". Справочное руководство по языку Фортран, том 1. Том 1. Silicon Graphics, Inc. 1999 [1993]. Номер документа: 007-3692-004. Архивировано из оригинала 2022-12-23 . Получено 2022-12-23 .(Примечание. Взято из "FORTRAN 90 HANDBOOK" (1992, McGraw-Hill, Inc. ) Уолтера С. Брейнерда, Джин К. Адамс, Джин Т. Мартин, Брайана Т. Смита и Джерролда Л. Вагенера.)
  10. ^ «Страуструп: часто задаваемые вопросы по стилю и технике C++».
  11. ^ Скотт Мейерс (2005). Эффективный C++, Третье издание . С. 21–23. Бостон: Addison Wesley. ISBN 978-0-321-33487-9 
  12. ^ "strchr, wcschr, _mbschr (CRT)". Msdn.microsoft.com . Получено 2017-11-23 .
  13. ^ «Деннис Ритчи: Почему мне не нравятся определители типа X3J11».
  14. ^ ab Язык программирования D, Андрей Александреску , 8.8: Распространение квалификатора от параметра к результату
  15. ^ "WG14-N3020: Функции стандартной библиотеки, сохраняющие квалификаторы" (PDF) . open-std.org . 2022-06-13. Архивировано (PDF) из оригинала 2022-10-13.
  16. ^ "const(FAQ) – Язык программирования D". Digitalmars.com . Получено 2013-08-18 .
  17. Бьярне Страуструп , «Расширения концепции типов языка C», внутренний технический меморандум Bell Labs, 5 января 1981 г.
  18. ^ abc Соперничество братьев и сестер: C и C++, Бьярне Страуструп , 2002, стр. 5
  19. ^ Деннис М. Ритчи , «Развитие языка C». Архивировано 15 июля 2012 г. на archive.today , 2003 г.: «X3J11 также ввел множество небольших дополнений и изменений, например, квалификаторы типов const и volatile , а также несколько иные правила продвижения типов».
  20. ^ "Позвольте мне начать с того, что я не убежден, что даже квалификаторы, существовавшие до декабря ('const' и 'volatile'), имеют свой вес; я подозреваю, что то, что они добавляют к стоимости изучения и использования языка, не компенсируется большей выразительностью. 'Volatile', в частности, является изюминкой для эзотерических приложений и гораздо лучше выражается другими средствами. Его главное достоинство в том, что почти каждый может забыть о нем. 'Const' одновременно более полезен и более навязчив; вы не можете избежать изучения его из-за его присутствия в интерфейсе библиотеки. Тем не менее, я не выступаю за искоренение квалификаторов, хотя бы потому, что уже слишком поздно".
  21. ^ Руководство Nim: раздел Const
  22. ^ const (Справочник по C#)
  23. ^ Гослинг, Джеймс; Джой, Билл; Стил, Гай. «Спецификация языка Java, третье издание».
  24. ^ "Идентификатор ошибки: JDK-4211070 Java должна поддерживать константные параметры (как C++) для поддержки кода [sic]". Bugs.sun.com . Получено 2014-11-04 .
  25. ^ 1815A [ неработающая ссылка ] , 3.2.1. Объявления объектов Архивировано 20 октября 2014 г. на Wayback Machine :
    «Объявленный объект является константой, если в объявлении объекта появляется зарезервированное слово constant; тогда объявление должно включать явную инициализацию. Значение константы не может быть изменено после инициализации. Формальные параметры режима in подпрограмм и записей, а также общие формальные параметры режима in также являются константами; параметр цикла является константой внутри соответствующего цикла; подкомпонент или срез константы является константой».
  26. ^ "const". MDN . Получено 2017-10-31 .

Внешние ссылки