Р.У. Себеста - Основные копцепции языков программирования (2001) (1160794), страница 50
Текст из файла (страница 50)
Даже при отсутствии рекурсии наличие доступной для подпрограмм локальной памяти с динамическим стеком имеет свои положительные стороны, поскольку для хранения локальных переменных все подпрограммы совместно используют одну область памяти. Недостатками автоматических переменных являются затраты времени на размешение в памяти и удаление из нее, а также то. что локальные переменные не могут зависеть от прелыстории. Языки ГОКТКАХ 77 и ГОКТКАЫ 90 позволяют разработчикам систем их реализации использовать для локальных вычислений автоматические переменные, но содержат оператор ЯАНЕ 1эаг Зтот оператор позволяет программисту указать, что некоторые или все перечисленные в списке 1эаг переменные в подпрограмме, содержашей данный оператор ЯАЧЕ.
будут статическими. В языках С и С++ локальные переменные по умолчанию являются автоматическими. В языках Рааса! и Ада все определенные в подпрограммах нединамические переменные являются автоматическими. Все атрибуты, кроме памяти, статически связываются с автоматическими переменными. Исключения (лля некоторых структурированных типов) рассматриваются в главе 5. Размещение в памяти и удаление из нее автоматических переменных описаны в главе 9. 4.4. Концепция связывания 185 Я.4.3.3.
Явные динамические переменные Явные динамические переменные — это безымянные (абстрактные) ячейки памяти. размещаемые и удаляемые с помошью явных команд периола выполнения, определяемых программистом. Обращаться к этим переменным можно только с помошью указателей н ссылок. Куча (оеар) представляет собой набор ячеек памяти с крайне неорганизованной структурой, вызванной непрелсказуемостью их использования. Явные динамические переменные создаются либо оператором (например в языках Ада и С++), либо вызовом предусмотренной для этого системной подпрограммы (например в языке С). Сушествуюшнй в языке С++ оператор распределения памяти иеи в качестве операнда использует нмя типа. При выполнении этого оператора создается явная динамическая переменная, имеюшая тнп операнда, и возврашается указатель на нее.
Поскольку явная динамическая переменная связывается с типом во время компиляции, то это связывание является статическим. Тем не менее, подобные переменные связываются с некоторой ячейкой памяти во время их создания, т.е. при выполнении пргнраммьь Помимо подпрограмм или операторов для создания явных динамических переменных, в некоторых языках есть средства их уничтожения.
В качестве примера явных динамических переменных рассмотрим следуюшнй фрагмент программы на языке С++: Дпв *1пСпобег 1пспос)е = пеи спс /* связывает ячейку 1пс */ с)е1есе 1пспос)ег /* освобождает ячейку, на которуа указывает указатель 1пхпос)е */ В этом примере явная динамическая переменная, имеюшая тип дпв, создается оператором пеи. Обращаться к этой переменной можно с помошью указателя дптззосге. Затем переменная удаляется из памяти оператором с)е1еве.
В объектно-ориентированном языке )ата все данные, за исключением основных скалярных величин, являются объектами. Объекты языка )ата представляют собой явные динамические объекты, и доступ к ним открывают ссылки, В языке )ача нет способа явного уничтожения динамических переменных; вместо этого используется неявная "сборка мусора". Явные динамические переменные часто используются в таких динамических структурах, как связные списки и деревья, которым необходимо расти и/или сокрашаться во время выполнения программы. Подобные структуры удобнее создавать с помощью указателей или ссылок, а также явных динамических переменных.
Недостатком явных динамических переменных является сложность корректного использования указателей и ссылок, а также стоимость ссылок на переменные, размещения в памяти и удаления из нее. Эти соображения, указатели и ссылки, а также методы реализании явных динамических переменных подробно рассмотрены в главе 5. 4.4.3.4. Неявные динамические переменные Неявные динамические переменные связываются с ячейкой динамической памяти только при присваивании им значений. Все их атрибуты фактически связываются каждый раз при присвоении переменным некоторого значения. Эти переменные, в некотором смысле, — просто имена, приспособленные для любого использования, которое от 166 Глава 4.
Имено, связывание, проверке типов и области видимости них потребуется. Плюсом таких переменных является их высокая степень гибкости, позволяющая писать крайне общие программы. Минусом являются затраты на поддержку во время выполнения программы всех динамических атрибутов. в число которых. помимо прочих. могут входить типы и диапазоны индексов массивов. Другим недостатком является потеря возможности обнаружения компилятором некоторых ошибок (см. раздел 4.4.2.2). В том же разделе приводятся примеры неявных динамических переменных в языке АРЬ. Примеры этих переменных в языке АЬООЬ 68 даны в главе 2, где они называются массивами й1ех. 4.5. Проверка типов Для обсуждения проверки типов обобщим понятия операндов и операторов, чтобы включить в эти понятия подпрограммы и операторы присваивания. Мы будем считать подпрограммы операторами, операндами которых являются их параметры.
Символ присваивания мы будем рассматривать как бинарный оператор, а операндами будут его целевая переменная и выражение. Проверка типов (гуре сйесЫп8) обеспечивает совместимость типов операндов оператора, Совместимым (сошрабие) типом называется тип, который либо изначально допускается для данного оператора, либо правила языка позволяют с помощью команд компилятора неявно преобразовать его в тип, допускаемый для данного оператора. Это автоматическое преобразование называется приведением (соегсюп).
Применение оператора к операнду неприемлемого типа называется ошибкой определении типа (гуре еггот. Если в языке все связывания переменных с типами являются статическими, то проверка типов практически всегда может выполняться статически. Динамическое связывание типов требует проверки типов во время выполнения программы, называющейся динамической проверкой типов. Некоторые языки, например АРЕ и БХОВОЕ4, имеющие динамическое связывание типов, допускают только динамическую проверку типов. Намного лучше обнаружить ошибки во время компилянии, чем во время выполнения, поскольку исправление на ранних стадиях, как правило, обходится дешевле.
Платой за статическую проверку типов является снижение гибкости программирования. При этом разрешается использовать значительно меньше сокрашений и уловок. Правда, подобные технические приемы пенятся сейчас не очень высоко. Проверка типов осложняется, если язык позволяет хранить в ячейке памяти в разное время выполнения программы величины разных типов.
Это можно сделать, например, с помощью вариантных записей языков Ада и Рааса(, оператора ЕЯ01НАЬЕЫСЕ языка РОСТКА)ч и объединений языков С и С++. В этих случаях проверка типов, если она производится, должна быть динамической, кроме того, она требует, чтобы система поддержки выполнения программ хранила типы текущих значений, записанных в таких ячейках памяти. Таким образом, даже если в языках, полобных С и Рааса!, все переменные статически связаны с типами, не все ошибки определения типа будут выявлены при статической проверке типов. 4.5.
Проверка типов 4.6. Строгая типизация Одной из новых идей в области структуры языков, проявившейся во время так называемой структурной революции в программировании !970-х годов, является строгая типизация (э!говд зур!пд). Строгая типизация общепризнанна как крайне полезная концепция. К сожалению, довольно часто она определяется неточно, а иногда в компьютерной литературе используется вообще без определения. Ниже следует простое, но неполное определение строго типизированного языка.
В программах, написанных на таком языке, каждое имя имеет отдельный связанный с ним тип, причем этот тип известен во время компиляции. Сутью этого опрелеления является то. что все типы связываются статически. Слабостью этого определения является игнорирование в нем следующей возможности: хотя тип переменной и может быть известен, ячейка памяти, с которой связана эта переменная. в разное время может содержать величины разных типов.
Для того чтобы учесть эту возможность. мы определим строго типизированный язык как такой, в котором всегда обнаруживаются ошибки типов. Для этого требуется, чтобы типы всех операндов могли опрелеляться либо во время компиляции, либо во время выполнения. Важность строгой типизации заключается в возможности выявления всех неправильных употреблений переменных, приводящих к ошибкам определения типов.