Реферат: Структуры и типы данных языка программирования
Структуры и типы данных языка программирования Тип данных — фундаментальное понятие теории программирования. Тип данных определяет множество значений, набор операций, которые можно применять к таким значениям и, возможно, способ реализации хранения значений и выполнения операций. Любые данные, которыми оперируют программы, относятся к определённым типам. 1.1ИсторияЕщё в 1960-х гг. Р. Хиндли (Roger Hindley) исследовал типизацию в комбинаторной логике. Его проблемной областью была типизация в языках, основаных на теории лямбда-исчисления. Позднее, в конце 1960-х годов, тот же учёный исследовал полиморфные системы типов. Позже, в 1970-х годах, Робин Милнер предложил практическую реализацию расширенной системы полиморфной типизации для языка функционального программирования ML.
1.2Определение
Тип (сорт) — относительно устойчивая и независимая совокупность элементов, которую можно выделить во всём рассматриваемом множестве (предметной области).[1]
Полиморфный тип — представление набора типов как единственного типа.
Математически тип может быть определён двумя способами:
Множеством всех значений, принадлежащим типу.
Предикатной функцией, определяющей принадлежность объекта к данному типу
^ 1.3Необходимость использования типов данных
Типы данных различаются начиная с нижних уровней системы. Так, например, даже в Ассемблере х86 различаются типы «целое число» и «вещественное число». Это объясняется тем, что для чисел рассматриваемых типов отводятся различные объёмы памяти, используются различные регистры микропроцессора, а для операций с ними применяются различные команды Ассемблера и различные ядра микропроцессора.
Концепция типа данных появилась в языках программирования высокого уровня как естественное отражение того факта, что обрабатываемые программой данные могут иметь различные множества допустимых значений, храниться в памяти компьютера различным образом, занимать различные объёмы памяти и обрабатываться с помощью различных команд процессора.
^ 1.4Практическое применение
Как правило, типы в языках программирования не всегда строго соответствуют подобным типам в математике. Например, тип «целое число» большинства языков программирования не соответствует принятому в математике типу «целое число», так как в математике указанный тип не имеет ограничений ни сверху, ни снизу, а в языках программирования эти ограничения есть. Как правило, в языках и системах имеется множество целых типов, отличающихся допустимым диапазоном значений (определяемым объёмом занимаемой памяти). Стоит отметить, что в большинстве реализаций языков и систем выход за границу целого типа (переполнение) не приводит к исключительной ситуации.
Современные языки программирования (включая Ассемблер) поддерживают оба способа задания типа (см. Определение). Так, в С++ тип enum является примером задания типа через набор значений. Определение класса (если рассматривать класс как тип данных) фактически является определением предиката типа, причём возможна проверка предиката как на этапе компиляции (проверка соответствия типов), так и на этапе выполнения (полиморфизм очень тесно связано с полиморфными типами). Для базовых типов подобные предикаты заданы создателями языка изначально.
^ 1.5Языки без типов
Теоретически не может существовать языков, в которых отсутствуют типы (включая полиморфные). Это следует из того, что все языки основаны на машине Тьюринга или налямбда-исчислении. И в том, и в другом случае необходимо оперировать как минимум одним типом данных — хранящимся на ленте (машина Тьюринга) или передаваемым и возвращаемым из функции (лямбда-исчисление).
^ 1.6Языки программирования по способу определения типов данных
Ниже перечислены языки программирования по способу определения типов данных:
1) Языки с полиморфным типом данных. Одни языки не связывают переменные, константы, формальные параметры и возвращаемые значения функций с определёнными типами, поддерживая единственный полиморфный тип данных. В чистом виде таких языков не встречается, но близкие примеры — MS Visual Basic — тип variant, Пролог,Лисп — списки. В этих языках переменная может принимать значение любого типа, в параметры функции можно передавать значения любых типов, и вернуть функция также может значение любого типа. Сопоставление типов значений переменных и параметров с применяемыми к ним операциями производится непосредственно при выполнении этих операций. Например, выражение a+b, может трактоваться как сложение чисел, если a и b имеют числовые значения, как конкатенация строк, если a и b имеют строковые значения, и как недопустимая (ошибочная) операция, если типы значений a и b несовместимы. Такой порядок называют «динамической типизацией» (соответствует понятиюполиморфизм в ООП, полиморфный тип в теории типов). Языки, поддерживающие только динамическую типизацию, называют иногда «бестиповыми». Это название не следует понимать как признак отсутствия понятия типов в языке — типы данных всё равно есть.
^ 2) Языки с неявным определением типов. Казалось бы, BASIC является примером языка без типов. Однако это строго типизированый язык: в нём различаются строковые типы (добавляется символ $), массивы (добавляется []) и числовые типы (ничего не добавляется).
^ 3) Языки с типом, определяемым пользователем. Также хорошо известны языки, в которых типы данных определяются автоматически, а не задаются пользователем. Каждой переменной, параметру, функции приписывается определённый тип данных. В этом случае для любого выражения возможность его выполнения и тип полученного значения могут быть определены без исполнения программы. Такой подход называют «статической типизацией». При этом правила обращения с переменными, выражениями и параметрами разных типов могут быть как очень строгими (С++), так и весьма либеральными (Си). Например, в классическом языке Си практически все типы данных совместимы — их можно применять совместно в любых выражениях, присваивать значение переменной одного типа переменной другого почти без ограничений. При таких операциях компилятор генерирует код, обеспечивающий преобразование типов, а логическая корректность такого преобразования остаётся на совести программиста. Подобные языки называют «языками со слабой типизацией». Противоположнось им — «языки с сильной типизацией», такие как Ада. В них каждая операция требует операндовстрого заданных типов. Никакие автоматические преобразования типов не поддерживаются — их можно выполнить только явно, с помощью соответствующих функций и операций. Сильная типизация делает процесс программирования более сложным, но даёт в результате программы, содержащие заметно меньше труднообнаруживаемых ошибок.
На практике языки программирования поддерживают несколько моделей определения типов одновременно.
^ 1.7Базовые типы
Каждый язык программирования поддерживает один или несколько встроенных типов данных (базовых типов) (см. классификацию типов данных), кроме того, развитые языки программирования предоставляют программисту возможность описывать собственные типы данных, комбинируя или расширяя существующие.
^ 1.8Преимущества от использования типов данных
Надёжность. Типы данных защищают от трёх видов ошибок:
Некорректное присваивание. Пусть переменная объявлена как имеющая числовой тип. Тогда попытка присвоить ей символьное или какое-либо другое значение в случае статической типизации приведёт к ошибке компиляции и не даст такой программе запуститься. В случае динамической типизации код программы перед выполнением потенциально опасного действия сравнит типы данных переменной и значения и также выдаст ошибку. Всё это позволяет избежать неправильной работы и «падения» программы.
Некорректная операция. Позволяет избежать попыток применения выражений вида «Hello world» + 1. Поскольку, как уже говорилось, все переменные в памяти хранятся как наборы битов, то при отсутствии типов подобная операция была выполнима (и могла дать результат вроде «ello worldǼ»). С использованием типов (см. далее «Контроль типов») такие ошибки отсекаются опять же на этапе компиляции.
Некорректная передача параметров. Если функция «синус» ожидает, что ей будет передан числовой аргумент, то передача ей в качестве параметра строки «Hello world» может иметь непредсказуемые последствия. При помощи контроля типов такие ошибки также отсекаются на этапе компиляции.
Стандартизация. Благодаря соглашениям о типах, поддерживаемых большинством систем программирования, сложилась ситуация, когда программисты могут быстро менять свои рабочие инструменты, а программы не требуют больших переделок при переносе исходных текстов в другую среду. К сожалению, стандартизации по универсальным типам данных ещё есть куда развиваться.
Документация. Использование того или другого типа данных объясняет намерения программиста. Например, enum и bool в языке не обязательны и вместо них может быть int — но оба они означают, что перед нами не целая величина, а одно из нескольких предопределённых значений.
^ 1.9Классификация типов данных
Типы данных бывают следующие:
Простые.
Перечислимый тип. Может хранить только те значения, которые прямо указаны в его описании.
Числовые. Хранятся числа. Могут применяться обычные арифметические операции.
Целочисленные: со знаком, то есть могут принимать как положительные, так и отрицательные значения; и без знака, то есть могут принимать только неотрицательные значения.
Вещественные: с запятой (то есть хранятся знак и цифры целой и дробной частей) и с плавающей запятой (то есть число приводится к виду m*be, где m — мантисса, b — основание показательной функции, e — показатель степени (порядок) (в англоязычной литературе экспонента), причём в нормальной форме 0<=mнормализованной форме 1<=mцелое число и хранятся знак и числа m и e).
Числа произвольной точности, обращение с которыми происходит посредством длинной арифметики. Примером языка с встроенной поддержкой таких типов являетсяUBASIC, часто применяемый среди криптографов.
^ Символьный тип. Хранит один символ. Могут использоваться различные кодировки.
^ Логический тип. Имеет два значения: истина и ложь, при троичной логике может иметь и третье значение — «не определено» (или «неизвестно»). Могут применятьсялогические операции. Используется в операторах ветвления и циклах. В некоторых языках является подтипом числового типа, при этом ложь=0, истина=1.
Множество. В основном совпадает с обычным математическим понятием множества. Допустимы стандартные операции с множествами и проверка на принадлежность элемента множеству. В некоторых языках рассматривается как составной тип.
^ Составные (сложные).
Массив. Является индексированным набором элементов одного типа. Одномерный массив — вектор, двумерный массив — матрица.
^ Строковый тип. Хранит строку символов. Аналогом сложения в строковой алгебре является конкатенация (прибавление одной строки в конец другой строки). В языках, близких к бинарному представлению данных, чаще рассматривается как массив символов, в языках более высокой абстракции зачастую выделяется в качестве простого.
Запись (структура). Набор различных элементов (полей записи), хранимый как единое целое. Возможен доступ к отдельным полям записи. Например, struct в C или record в Pascal.
^ Файловый тип. Хранит только однотипные значения, доступ к которым осуществляется только последовательно (файл с произвольным доступом, включённый в некоторые системы программирования, фактически является неявным массивом).
Класс.
Другие типы данных. Если описанные выше типы данных представляли какие-либо объекты реального мира, то рассматриваемые здесь типы данных представляют объекты компьютерного мира, то есть являются исключительно компьютерными терминами.
Указатель. Хранит адрес в памяти компьютера, указывающий на какую-либо информацию, как правило — указатель на переменную.
Ссылка.
^ 1.10 Контроль типов и системы типизации
Процесс проверки и накладывания ограничений типов — контроля типов, может выполняться во время компилирования (статическая проверка) или во время выполнения (динамическая проверка).
^ Статическая типизация — контроль типов осуществляется при компиляции.
Динамическая типизация — контроль типов осуществляется во время выполнения.
Контроль типов также может быть строгим и слабым.
^ Строгая типизация — совместимость типов автоматически контролируется транслятором:
^ Номинативная типизация (англ. nominative type system) — совместимость должна быть явно указана (наследована) при определении типа.
^ Структурная типизация (англ. structural type system) — совместимость определяется структурой самого типа (типами элементов, из которых построен составной тип).
^ Слабая типизация — совместимость типов никак транслятором не контролируется. В языках со слабой типизацией обычно используется подход под названием «утиная типизация» — когда совместимость определяется и реализуется общим интерфейсом доступа к данным типа.
^ 2. Типы и структуры данных
Типы и структуры данных представляют собой фундамент, на котором строится вся современная технология программирования. Программирования в широком смысле, включая не только непосредственно написание и отладку программ, но и проектирование программных систем разной сложности; проектирование, реализацию и использование баз данных и информационных систем и т.д. Сегодня только большие любители обходятся без использования безтиповых языков программирования (например, языков ассемблера) или неструктурированных и/или нетипизированных хранилищ данных во внешней памяти. В этой части книги, не прибегая к излишним формализмам и теоретическим изыскам, мы приводим систематическое обсуждение основных типов и структур данных, применяемых в современных языках программирования, а также соответствующих концепций, используемых в распространенных реляционных и перспективных объектно-реляционных системах.
^ 2.2. Понятие типа данных
Существует много подходов к определению понятия типа данных от полностью математических, основанных на аппаратах абстрактной алгебры или математической логики, до полностью житейских, ориентированных исключительно на интуицию. Автору книги ближе всего подход, применяемый классиком компьютерной литературы и создателем ряда исключительно стройных и красивых языков программирования Никласом Виртом.
Основным принципом типизации, принятым в языках программирования и базах данных является то, что любая константа, переменная, выражение и функция относится к некоторому типу, характеризующему прежде всего множество значений, к которым относятся константы, которые могут принимать переменные и выражения и которые могут формировать функции. При описании любых используемых констант, переменных и функций явно или неявно указывается их тип. В первую очередь это дает возможность компилятору и/или системе управления базами данных выделить для хранения объекта данных ровно тот объем памяти, который определяется допустимым диапазоном значений типа. Однако концепция типа этим не исчерпывается.
Следующим исключительно важным свойством типа данных является инкапсуляция внутреннего представления его значений. К значению типа данных (значения констант, переменных, выражений и функций) можно обращаться только с помощью операций, предопределенных в описании этого типа. Эти операции могут быть явными (например, арифметические операции "+", "-", "?" и "/" для числовых типов) или неявными (например, операция преобразования значения целого типа к значению плавающего типа; заметим, что в некоторых языках, в частности, в Си и Си++, допускаются и явные преобразования типов).
Наличие типовых описаний констант, переменных и функций и предписанные правила определения типов выражений вместе с поддержкой свойства инкапсуляции типов дают возможность компиляторам языков программирования и языков баз данных производить существенный контроль допустимости языковых конструкций на этапе компиляции, что позволяет сократить число проверок на стадии выполнения программ и облегчить их отладку.
Один из характерных примеров преимущества использования типизированных языков программирования представляет история операционной системы UNIX. Как известно, система первоначально была написана на языке ассемблера PDP-7. При переходе к использованию PDP-11 ОС UNIX была переписана на языке более высокого уровня B, который являлся прямым наследником безтипового языка программирования BCPL. В очень скором времени по мере роста размеров системы ее разработчикам стало понятно, что бесчисленные проверки времени выполнения очень усложняют отладку и замедляют работу системы. Это явилось исходным толчком к внедрению в язык B системы типов и созданию типизированного языка Си, опора на который обеспечила более чем 25-летнюю плодотворную жизнь системы.
Можно приводить различные классификации типов данных, например, простые и составные типы, предопределенные и определяемые типы и т.д. Существенно то, что несмотря на многолетнее использование типов данных в отечественном программировании, так и не сложилась устойчивая и общепринятая русскоязычная терминология. Поэтому в этой книге будем использовать некоторый набор терминов, выбранных из соображений максимальной распространенности и интуитивной ясности.
Выделим следующие категории типов:
Встроенные типы данных, т.е. типы, предопределенные в языке программирования или языке баз данных. Обычно в языке фиксируются внешнее представление значений этих типов (вид литеральных констант) и набор операций с описанием их семантики. Внутреннее представление и реализация операций выбираются в конкретных компиляторах и подсистемах поддержки выполнения программ.
Под термином "уточняемый тип данных" мы понимаем возможность определения типа на основе встроенного типа данных, значения которого упорядочены. В частности, к категории уточняемых типов относится тип поддиапазона целых чисел в языках линии Паскаль.
Категорию перечисляемых типов данных составляют явно определяемые целые типы с конечным числом именованных значений. Это очень простой и легко реализуемый механизм, часто являющийся очень полезным.
Замечание: использование уточняемых и перечисляемых типов порождает потребность в динамической проверке корректности значений - выхода значения за пределы явно (в случае уточняемых типов) или неявно (в случае перечисляемых типов) диапазона.
Конструируемые типы (иногда их называют составными) обладают той особенностью, что в языке предопределены средства спецификации таких типов и некоторый набор операций, дающих возможность доступа к компонентам составных значений. Мы обсудим наиболее распространенные разновидности конструируемых типов: типы массивов, записей и множеств, а также различия в понимании этих типов в разных языках.
Замечание: будучи согласны с переводчиком книги Никласа Вирта Д.Б.Подшиваловым в том, что русские термины "тип массива", "тип записи", "тип множества" и т.д. не совсем соответствуют английским оригиналам "array type", "record type", "set type" и т.д. мы все же не будем использовать рекомендуемые им термины "записной тип", "массивный тип" и "множественный тип", поскольку (a) они тоже не вполне соответствуют оригинальным терминам и (b) ужасно выглядят и произносятся.
Указательные типы дают возможность работы с типизированными множествами абстрактных адресов переменных, содержащих значения некоторого типа. В сильно типизированных языках (Паскаль, Модула, Ада и т.д.) работа с указателями сильно ограничена. В частности, невозможно получить значение указателя явно определенной переменной и/или применять к известным значениям указателей адресную арифметику. В языках с более слабой типизацией (например, Си/Си++) допускаются практически неограниченные манипуляции указателями.
Вообще говоря, упоминавшиеся выше уточняемые, перечисляемые и конструируемые типы данных являются типами, определяемыми пользователями. Но эти определения не могут включать спецификацию операций над значениями типов. Допустимые операции либо предопределены, либо наследуются от некоторого определенного ранее или встроенного типа. Под термином "определяемый пользователем тип данных" (ранее был больше распространен термин "абстрактный тип данных", однако мы не будем здесь его использовать, поскольку, на наш взгляд, он не точно отражает смысл понятия) мы будем понимать возможность полного определения нового типа, включая явную или неявную спецификацию множества значений, спецификацию внутреннего представления значений типа и спецификацию набора операций над значениями определяемого типа.
Наконец, под термином "полнотиповая система" мы понимаем систему типов, в которых типы, определяемые пользователем, равноправны с предопределенными типами, т.е. можно, например, определить тип массива с элементами любого определенного типа, можно использовать определяемый пользователем тип на основе любого определенного типа и т.д.
^ 2.2. Встроенные типы данных
Обычно в состав встроенных типов данных включаются такие типы, операции над значениями которых напрямую или, по крайней мере, достаточно эффективно поддерживаются командами компьютеров. В современных компьютерах к таким "машинным" типам относятся целые числа разного размера (от одного до восьми байт), булевские значения (поддерживаемые обычно за счет наличия признаков условной передачи управления) и числа с плавающей точкой одинарной и двойной точности (обычно четыре и восемь байт соответственно). В более ранних компьютерах часто поддерживалась десятичная арифметика с фиксированной точкой (например, в мейнфреймах компании IBM и супер-миникомпьютерах компании Digital), но в настоящее время прямая аппаратная поддержка такой арифметики отсутствует практически во всех распространенных процессорах.
В соответствии с этим, в традиционный набор встроенных типов обычно входят следующие (мы будем говорить про размеры внутреннего представления значений этих типов, хотя в спецификациях языков такая информация, как правило, отсутствует):
Тип CHARACTER (или CHAR) в разных языках - это
либо набор печатных символов из алфавита, зафиксированного в описании языка (для большинства языков англоязычного происхождения этот алфавит соответствует кодовому набору ASCII);
либо произвольная комбинация нулей и единиц, размещаемых в одном байте.
В первой интерпретации (свойственной языкам линии Паскаль) для значений типа CHAR определены только операции сравнения в соответствии с принятым алфавитом. Например, при использовании ASCII выполняются соотношения 0 < 1 < ...< 9 < A < B < ...< Z < a < b < ...< z; известно, что если значение переменной x удовлетворяет условию 0 <= x <= 9, то это значение - цифра; если A <= x <= Z, то значение x - прописная буква; если a <= x <= z, то значение x - строчная буква и т.д. При использовании этой интерпретации арифметические операции над символьными значениями не допускаются.
Во второй интерпретации (свойственной языкам линии Си) литеральными константами типа CHAR по-прежнему могут быть печатные символы из принятого в языке алфавита, но возможно использование и числовых констант, задающих желаемое содержимое байта. В этом случае, как правило, над значениями типа CHAR возможно выполнение не только операций сравнения, но и операций целочисленной арифметики.
Наконец, в некоторых языках явно различают тип CHAR как чисто символьный тип и тип сверхмалых целых (TINY INTEGER) как тип целых чисел со значениями, умещающимися в один байт.
В современных компьютерах, как правило, поддерживается целочисленная байтовая арифметика, обеспечивающая как первую, так и вторую интерпретацию типа CHAR.
Тип BOOLEAN в тех языках, где он явно поддерживается, содержит два значения - TRUE (истина) и FALSE (ложь). Несмотря на то, что для хранения значений этого типа теоретически достаточно одного бита, обычно в реализациях переменные этого типа занимают один байт памяти. Для всех типов данных, для которых определены операции сравнения, определены также и правила, по которым эти операции сравнения вырабатывают булевские значения. Над булевскими значениями возможны операции конъюнкции (& или AND), дизъюнкции (| или OR) и отрицания (~ или NOT), определяемые следующими таблицами истинности:
TRUE AND TRUE = TRUE
TRUE AND FALSE = FALSE
FALSE AND TRUE = FALSE
FALSE AND FALSE = FALSE
TRUE OR TRUE = TRUE
TRUE OR FALSE = TRUE
FALSE OR TRUE = TRUE
FALSE OR FALSE = FALSE
NOT FALSE = TRUE
NOT TRUE = FALSE
При работе с булевскими значениями в языках баз данных некоторую проблему вызывает то, что по причине возможности хранения в базе данных неопределенных значений операции сравнения могут вырабатывать не два, а три логических значения: TRUE, FALSE и UNKNOWN. Поэтому в языке SQL-92, например, используется не двухзначная, а трехзначная логика, в результате чего логические операции при их обработке в серверах баз данных определяются расширенными таблицами (мы приводим их с учетом коммутативности двуместных операций):
TRUE AND TRUE = TRUE
TRUE AND FALSE = FALSE
TRUE AND UNKNOWN = UNKNOWN
FALSE AND UNKNOWN = FALSE
TRUE OR TRUE = TRUE
TRUE OR FALSE = TRUE
TRUE OR UNKNOWN = TRUE
FALSE OR UNKNOWN = UNKNOWN
NOT FALSE = TRUE
NOT TRUE = FALSE
NOT UNKNOWN = UNKNOWN
Помимо общего возрастания сложности и недостаточной удовлетворительности трехзначной логики для целей работы с базами данных, неприятность состоит в отсутствии поддержки этой логики в языках программирования (как, впрочем, и в отсутствии явной поддержки неопределенных значений).
В языках линии Си прямая поддержка булевского типа данных отсутствует, но имеется логическая интерпретация значений целых типов. Значением операции сравнения может быть "0" (FALSE) или "1" (TRUE). Значение целого типа "0" интерпретируется как FALSE, а значения, отличные от нуля, - как TRUE. В остальном все работает как в случае наличия явной поддержки булевского типа.
Тип целых чисел в общем случае включает подмножество целых чисел, определяемое числом разрядов, которое используется для внутреннего представления значений. При определении типа целых чисел обычно стремятся к тому, чтобы множество его значений было симметрично относительно нуля (собственно, это стимулируется и стандартными свойствами машинной целочисленной арифметики). Поэтому приходится тратить один бит на значение знака числа и при использовании n бит для внутреннего представления целого соответствующий тип содержит значения в диапазоне от -2(n-1) до 2(n-1). В подавляющем большинстве современных процессоров отрицательные целые числа обычно представляют в дополнительном коде.
В языках, ориентированных на 32-разрядные компьютеры, в частности, в стандартных Си и Си++ для рационального использования памяти допускаются модификации целого типа short integer (обычно 16-разрядные), integer (обычно то же самое, что и long integer) и long integer (обычно 32-разрядные), а также байтовые целые (char). При этом поддерживаются автоматические преобразования значений типов меньшего размера к значениям типов большего размера. Пока не очень понятно, какие встроенные целые типы будут зафиксированы в будущем "64-разрядном" стандарте языка Си, но многие компании считают разумным использовать модель под названием LP64, в которой предполагается размер char - 8 бит, размер short integer - 16 бит, размер integer - 32 бита и размер long integer и long long integer - 64 бита.
Наряду со знаковыми целыми типами в языках часто поддерживаются беззнаковые целые. Такие типы в линии языков Паскаль называются CARDINAL, а в линии языков Си именуются путем добавления модификатора unsigned к названию соответствующего целого типа. Таким образом, в последнем случае существуют типы unsigned char, unsigned short integer, unsigned integer и unsigned long integer. Поскольку множество значений типа unsigned в два раза мощнее множества значений соответствующего целого типа, то поддерживается их автоматическое преобразование только к целым типам большего размера.
Наконец, для поддержки численных вычислений в языках обычно специфицируется встроенный тип чисел с плавающей точкой с базовым названием REAL или FLOAT. Обычно в описании языков не фиксируется диапазон и точность значений такого типа. Они уточняются в реализации и обычно существенно зависят от особенностей целевого процессора. В языках семейства Си (32-разрядных) специфицированы три разновидности типа чисел с плавающей точкой - float (обычно с размером 16 бит), double float (размером в 32 бит) и long double float (размером 64 бит).
^ 2.3. Уточняемые типы данных
Никлас Вирт называет такие типы ограниченными (restricted). На самом деле, ни этот термин, ни тот, который употребляем мы в этой книге, не являются абсолютно правильно отражающими суть соответствующего механизма. Все же, по нашему мнению, термин "уточняемый тип" немного ближе по смыслу.
Суть состоит в том, что для любого значения любого встроенного (и перечисляемого) типа существует его внешнее литеральное представление. Более того, по литеральному представлению константы можно однозначно определить тип, к которому она относится. Если к тому же на множестве значений типа задано отношение порядка (определены операции сравнения), то иногда возникает потребность сказать, что в данном приложении нас интересует подмножество значений такого типа, ограниченное некоторым специфицированным диапазоном. По причине наличия упорядоченности значений такой диапазон может быть задан парой литеральных констант базового типа c1 и c2, удовлетворяющих условию c1 <= c2. Тем самым, определение нового уточненного типа может иметь вид (пример из языка Модула-2): TYPE T = [c2..c2].
Почему мы предпочитаем использовать термин "уточняемый тип"? Основная причина состоит в том, что "ограниченные типы" в том смысле, в котором они используются в языках линии Паскаль, являются частным случаем более общего понятия, используемого в языках баз данных и именуемого "доменом". При определении домена тоже накладывается некоторое ограничение на значения базового типа, но это ограничение может выражаться в виде произвольного логического выражения, а не только с помощью указания диапазона. То есть мы действительно уточняем характеристики базового типа.
Основной проблемой уточняемых типов является потребность в динамическом контроле значений, формируемых при вычислении выражений и возвращаемых функциями. Если для значений базовых типов (по крайней мере, числовых) такой контроль, как правило, поддерживается аппаратурой компьютера, то для уточняемых типов, вообще говоря, требуется программный контроль, вызывающий серьезные накладные расходы. В развитых компиляторах обычно поддерживаются два режима компиляции - отладочный со всеми возможными контролирующими действиями во время выполнения программы и "боевой", в котором контроль отключается. Однако, если учесть, что в любой серьезной программе ошибки сохраняются на протяжении всей ее жизни, бесконтрольное выполнение программ очень затрудняет нахождение таких ошибок.
^ 2.4. Перечисляемые типы данных
Перечисляемый тип состоит из конечного числа упорядоченных именованных значений. В классическом варианте, свойственном, например, языкам линии Паскаль, определение типа состоит из перечисления имен значений (поэтому справедливо называть такой тип перечисляемым), эти имена в дальнейшем играют роль имен литеральных констант этого типа и должны отличаться от литерального изображения констант любого другого типа. Поскольку значения типа задаются путем перечисления, каждому значению можно однозначно сопоставить натуральное число от 1 до n, где n - число значений перечисляемого типа.
Обычно для любого перечисляемого типа предопределяются операции получения значения по его номеру и получения номера по значению. Кроме того, для перечисляемого типа предопределяются операции сравнения и получения следующего и предыдущего значения. По причине однозначного сопоставления значению перечисляемого типа натурального числа, возможно неявное преобразование этих значений к значению любого числового типа данных.
В языках линии Си под тем же термином "перечисляемый тип" понимается нечто другое, поскольку при определении такого типа можно явно сопоставить имени значения некоторое целое (не обязательно положительное) число; при отсутствии явного задания целого первому элементу перечисляемого типа неявно соответствует 0, а каждому следующему - целое значение, на единицу большее целого значения предыдущего элемента. При этом (a) использование имени перечисляемого типа для объявления переменной эквивалентно использованию типа integer, и такая переменная может содержать любое целое значение; (b) имена значений перечисляемого типа на самом деле понимаются как имена целых констант, и к этим значениям применимы все операции над целыми числами, даже если они выводят за пределы множества целых значений элементов перечисляемого типа. Так что перечисляемый тип в смысле языка Си - это не совсем тип в строгом смысле этого слова, а скорее удобное задание группы именованных констант целого типа.
^ 2.5. Конструируемые типы данных
Мы переходим к рассмотрению группы разновидностей типов данных, которые в литературе часто называют "составными", поскольку любое значение любого из этих типов состоит из значений одного или нескольких других типов. Мы предпочитаем использовать термин "конструируемый тип", поскольку для каждой разновидности типов этой группы в языке программирования специфицируются средства построения (конструирования) нового типа на основе встроенных и/или ранее определенных типов, и для каждой разновидности предопределяются операции, позволяющие извлечь компонент составного значения. К наиболее распространенным конструируемым типам относятся тип массива, тип записи и тип множества.
2.5.2. Массивы
Как и в ряде предыдущих разделов, понятия массива и типа массива сильно различаются в сильно и слабо типизированных языках. Начнем с классического понятия в сильно типизированных языках (например, в языке Паскаль). Тип массива в таких языках определяется на основе двух вспомогательных типов: типа элементов массива (базового типа) и типа индекса массива. В языке Паскаль определение типа массива выглядит следующим образом: type T = array [I] of T0, где T0 - базовый тип, а I - тип индекса. T0 может быть любым встроенным или ранее определенным типом. Тип индекса I должен состоять из конечного числа перечисляемых значений, т.е. быть уточненным, перечисляемым, символьным или булевским типом. В языках линии Паскаль допускается и неявное определение уточненного типа массива. Например, допустимы следующие определения типа массива: type T = array [2..6] of integer или type T = array ['a'..'e'] of real.
Если мощность множества значений типа индекса есть n, то значение типа массива - это регулярная структура, включающая n элементов базового типа. Соответствующим образом устроены и переменные типа массива. Для любого сконструированного типа массива предопределены две операции - операция конструирования значения типа массива и операция выборки элемента массива. Если x - переменная типа массива T, а i - значение соответствующего типа индекса, то для конструирования значения используется языковое средство x:= T (c1, c2, ..., cn), где c1, c2, ..., cn - значения базового типа. Для выборки элемента массива используется конструкция x[i], значением которой является значение i-того элемента массива (вместо i в квадратных скобках может содержаться любое допустимое выражение, значение которого принадлежит множеству значений типа индекса). Эта же конструкция может использоваться в левой части оператора присваивания, т.е. элементы массива могут изменяться индивидуально. Кроме того, при подобной строгой типизации массивов допустимы присваивания значений переменных типа массива, функции, возвращающие значение типа массива и т.п.
Базовым типом типа массива может быть любой встроенный или определенный тип, в том числе и тип массива. В последнем случае говорят о многомерных массивах или матрицах. Для работы с мног
еще рефераты
Еще работы по разное