
Лекция 14
4. Типы данных
В этом разделе вводится концепция типа данных и рассматриваются свойства основных типов. В частности, описывается устройство перечислимых и ограниченных типов, исследуются структурные типы данных. Особое внимание уделяется массивам, записям и объединениям. В заключение рассматриваются множественные и ссылочные типы.
На структуру типов значительное влияние оказывают методы их реализации. По этой причине в раздел включена еще одна часть, касающаяся реализации типов данных, в частности, массивов.
4.1. Введение в типы
Компьютерные программы обрабатывают данные и получают результаты. Эффективность этого процесса зависит от того, насколько типы данных соответствуют реальным задачам. Важно, чтобы в языке была предусмотрена поддержка соответствующего разнообразия типов и структур данных.
Концепции типов развиваются на протяжении последних 50 лет. В ранних языках программирования данные моделировались небольшим количеством основных структур, поддерживаемых этими языками. Например, в ранних версиях языка FORTRAN связные списки и двоичные деревья обычно реализовывались с помощью массивов.
Первый шаг в сторону от такой модели был сделан разработчиками структур данных в языке COBOL. Программистам было позволено устанавливать точность десятичных чисел и использовать структурные типы данных. В языке PL/I возможность устанавливать точность целочисленных величин и чисел с плавающей точкой была расширена. Позднее аналогичные средства были включены в языки Ada и FORTRAN 90.
Разработчики PL/I определили в языке много различных типов данных. В языке ALGOL 68, напротив, было представлено небольшое количество основных типов. Однако были реализованы операторы, обеспечивающие гибкое описание структур, что позволило создавать определяемые пользователем типы данных, приспосабливая их к поставленной задаче. Это было одним из самых значительных достижений в области разработки типов.
Определяемые пользователем типы улучшают читабельность программ, поскольку для них можно использовать осмысленные имена. Они допускают проверку типов переменных. Кроме того, эти типы облегчают модификацию программ: программист может модифицировать тип некоторой категории переменных, изменив оператор объявления типов.
Появившиеся в конце 1970-х годов концепции типов, определяемых пользователем, были обобщены и реализованы в языке Ada 83 как абстрактные типы данных. Согласно этой методологии, программисту позволяется создавать отдельный тип для каждого класса переменных, определяемых предметной областью задачи. Более того, язык должен обеспечивать уникальность типов, фактически являющихся абстракциями переменных из предметной области. Идея, лежащая в основе абстрактного типа данных, заключается в отделении использования типа от способа представления переменных этого типа. Все типы данных, предусмотренные в высокоуровневых языках программирования, являются абстрактными.
Наиболее распространенными структурными типами данных являются массивы и записи. Они, как и некоторые другие типы данных, задаются операторами типов или конструкторами. В качестве примера операторов типа можно назвать существующие в языке C круглые и квадратные скобки, а также звездочки, используемые для задания массивов, функций и указателей.
О переменных часто говорят в терминах дескрипторов. Дескриптором называется совокупность атрибутов переменной, реализуемая в виде набора содержащих эти атрибуты ячеек памяти. Если все переменные являются статическими, то дескрипторы нужны только во время компиляции. Статические дескрипторы обычно создаются компилятором в виде части таблицы идентификаторов и используются во время компиляции. Динамические атрибуты, в свою очередь, нуждаются в динамическом дескрипторе (или его части) во время выполнения программы. В этом случае дескриптор используется системой поддержки выполнения программ. И статические, и динамические дескрипторы используются для проверки типов, а также в операциях размещения переменных в памяти и удаления из нее.
Со значением переменной и занимаемой ею памятью в литературе часто ассоциируется термин «объект». В данном курсе под словом «объект» подразумеваются экземпляры абстрактных типов данных, определяемых пользователем. В объектно-ориентированных языках программирования объектом называется любой экземпляр любого встроенного или определенного пользователем класса.
В последующих разделах рассмотрены широко распространенные типы данных. Для всех типов основным является вопрос о том, какие операции предусмотрены с переменными данного типа и как они задаются.
4.2. Элементарные типы данных
Типы данных, не определяемые в терминах других типов, называются элементарными. Большинство языков имеют определенный набор элементарных типов данных. Некоторые из них являются отображением особенностей аппаратного обеспечения – например, целые числа. Реализация других типов требует незначительной программной поддержки.
4.2.1. Числовые типы
Во многих ранних языках программирования существовали только числовые элементарные типы. В современных языках эти типы по-прежнему играют существенную роль.
4.2.1.1. Целые числа
Наиболее распространенным элементарным числовым типом является целое число. Многие компьютеры поддерживают несколько размеров целых чисел, и эти возможности нашли отражение в языках программирования. В реализациях языка Ada, например, может содержаться до трех размеров целых чисел: SHORT INTEGER, INTEGER и LONG INTEGER. Некоторые языки, например C, содержат целые числа без знаков.
Целые числа представляются в компьютере в виде строки битов, причем один из битов (как правило, крайний слева) часто представляет знак. Целые типы поддерживаются непосредственно аппаратным обеспечением.
В большинстве современных компьютеров для хранения отрицательных целых чисел используется дополнительный код числа, удобный для выполнения операций сложения и вычитания. Дополнительный код отрицательного целого числа в двоичной системе образуется путем логического дополнения положительного числа и прибавления к нему единицы. В некоторых компьютерах все еще используется другое представление, а именно: обратный код числа в двоичной системе. При такой записи отрицательное значение целого числа хранится как логическое дополнение к его абсолютному значению. Недостатком представления в виде обратного кода является наличие двух форм записи числа 0.
4.2.1.2. Числа с плавающей точкой
Действительные числа моделируются в компьютере типами чисел с плавающей точкой, однако представление большинства этих чисел является лишь аппроксимацией. Например, фундаментальные числа π и e не могут точно представляться числами с плавающей точкой. В большинстве компьютеров числа с плавающей точкой хранятся в двоичных кодах, что еще усугубляет проблему их записи. Например, даже десятичную величину 0.1 нельзя представить конечным набором двоичных чисел. Другой проблемой использования чисел с плавающей точкой является потеря точности при арифметических операциях.
Числа с плавающей точкой кодируются в форме мантиссы и порядка, перенятой из научной записи. Ранее компьютеры использовали различные представления величин с плавающей точкой, но в наше время в большинстве случаев используется формат IEEE Floating-Point Standard 754. Разработчики языков программирования используют любое представление, поддерживаемое аппаратным обеспечением. Большинство языков содержит два типа чисел с плавающей точкой, часто называемых float и double. Переменные типа float имеют стандартный размер, равный, как правило, четырем байтам памяти. Тип double используется в ситуациях, требующих большей по размеру мантиссы. Относящиеся к этому типу переменные с удвоенной точностью записи обычно занимают вдвое больше памяти, чем обычные переменные, и имеют как минимум вдвое больше битов в мантиссе.
Множество величин, которые можно представить с помощью чисел с плавающей точкой, определяется их точностью и диапазоном. Точность числа – это точность его мантиссы, измеряемая числом битов, а в понятие диапазона входит диапазон изменения мантиссы и, что более важно, диапазон изменения порядка.
Аппаратное обеспечение некоторых небольших компьютеров не поддерживает операции с плавающей точкой. На таких машинах эти операции моделируются с помощью программного обеспечения, что может замедлять их выполнение в 10–100 раз по сравнению с аналогичными аппаратными операциями.
4.2.1.3. Десятичные числа
Компьютеры, предназначенные для коммерческих приложений, часто поддерживают типы десятичных чисел (decimal). К этим типам относятся числа с фиксированным количеством десятичных знаков и десятичной точкой в установленном месте. Такие типы в коммерческих приложениях являются основными. Они играют существенную роль в языке COBOL.
Достоинством десятичных чисел является способность (в отличие от типов с плавающей точкой) содержать точные значения десятичных величин, по крайней мере, из ограниченного диапазона. Недостатками десятичных типов является ограниченный диапазон изменения переменных вследствие отсутствия показателей степени и неэкономного представления в памяти.
Десятичные числа, как и строки символов, записываются в памяти с помощью двоичных кодов десятичных цифр. Такие представления называются двоично-кодированными десятичными числами (BCD). В некоторых случаях десятичные величины запоминаются в виде одной цифры на байт, а в других байт содержит две цифры. В любом случае памяти для запоминания числа требуется больше, чем при двоичных представлениях.
Рассмотрим пример. Кодировка десятичной цифры требует не менее 4 бит. Следовательно, для запоминания шестиразрядного десятичного числа потребуется 24 бит памяти. В то же время запоминание этого же числа в двоичном представлении требует всего 20 бит. Операции над десятичными величинами производятся аппаратным обеспечением машин, имеющих такие возможности, либо моделируются программным обеспечением.