какие существуют типы данных пользователя
Лекция 6. Пользовательские типы данных¶
Простые типы¶
Классификация¶
Стандартные и нестандартные типы¶
Использование только стандартных типов в программе уменьшает её возможности.
Стандартные типы не обладают информативностью.
Нестандартные типы данных получаются с помощью специальных механизмов.
Описания нестандартных типов¶
Описания нестандартных типов обычно размещают:
Псевдонимы¶
Упрощение сложных объявлений¶
Объявить массив из N указателей на функции, возвращающих указатели на функции, возвращающие указатель на char.
Можно объявить этот массив так:
А можно воспользоваться typedef:
Перечисления¶
Основой для внутреннего представления перечислимого типа является тип int.
Слабая типизация С позволяет смешивать в программе числовые константы и перечислимые типы, а также использовать арифметические операции (++).
В С++ (с сильной типизацией) данный пример вызовет ошибку и будет требовать перегрузки арифметического оператора ++ для перечислимого типа.
Как связать перечисление со строкой¶
Составные типы¶
Структуры¶
Записи¶
Структура (запись) представляет собой набор данных, хранящихся в памяти в смежных адресах, но не обязательно принадлежащих одному типу. Это позволяет рассматривать саму структуру как универсальный тип для представления внутреннего устройства множества объектов.
Изображение структуры (записи):
Фундаментальные структуры данных¶
Замечательно то, что массивы и записи можно объединять, создавая:
На основе массивов и записей строят:
Определение структуры¶
Сначала мы описываем новый структурный тип:
Типы данных, определенные пользователем
Тип данных, определенный пользователем (UDT – User Defined Type)– это именованный тип данных, создаваемый на основе существующего типа, но, тем не менее, рассматриваемый как несовместимый с ним. В DB2 существует две разновидности типов данных, определенных пользователем:
• особый (distinct) тип, который разделяет внутреннее представление с некоторым встроенным типом данных DB2,
• структурный тип, который представляет последовательность именованных атрибутов, каждый из которых имеет свой тип.
Мы ограничимся здесь рассмотрением особых типов данных, определенных пользователем.
Особые типы данных, определенные пользователем, базируются на некотором существующем встроенном типе данных DB2. Особый тип данных имеет такое же внутреннее представление, как и существующий встроенный тип данных (исходный тип), но рассматривается как отдельный тип, несовместимый с исходным типом. Например, особые типы могут быть использованы для представления валюты разных стран.
Возможность создания собственных особых типов данных дает ряд преимуществ:
1. Определяя новые типы, можно расширить множество типов данных, предоставляемых DB2 для реализации прикладной системы.
2. Для нового типа данных можно, используя функции, определенные пользователем (UDF – User-Defined Functions), определить свою семантику и свое поведение.
3. Сильная типизация, используемая DB2, гарантирует, что особый тип данных будет использован надлежащим образом – только функции, явно определенные для конкретного особого типа данных, может обрабатывать значения соответствующего типа.
Особый тип данных, определенный пользователем, создается с помощью предложения CREATE DISTINCT TYPE, имеющего следующий синтаксис:
CREATE DISTINCT TYPE имя_нового_типаASисходный_тип_данныхWITH COMPARISON
Исходный тип данных указывает основу для внутреннего представления значений нового типа данных. В качестве исходного типа данных могут быть указаны только встроенные типы данных.
При создании нового типа также генерируются функции преобразования из исходного типа в новый тип, и наоборот. Имена генерируемых функций преобразования совпадают с именами типов данных. Так, если src – определяет значение исходного типа данных, тогда эквивалентное значение нового типа можно получить с помощью сгенерированной функции имя_нового_типа(src); если trgt – значение нового типа, эквивалентное значение исходного типа можно получить с помощью функции исходный_тип_данных(trgt).
Определенный пользователем особый тип данных может использоваться, наравне со встроенными типами данных, при создании таблиц (в предложении CREATE TABLE).
Примеры:
Пример 1. Создать особый тип данных с именем MYOWN на основе типа INTEGER:
CREATE DISTINCT TYPE MYOWN AS INTEGER WITH COMPARISON
Пример 2. Создать особый тип данных с именем MILES на основе типа DOUBLE.
CREATE DISTINCT TYPEMILES AS DOUBLE WITH COMPARISONS
Пользовательские типы данных
К пользовательским типам данных относятся нестандартные данные, о структуре которых система не имеет представления, и операции над которыми стандартом языка C не определены. Структура таких данных становится известной компилятору только по описанию, содержащемуся в тексте исходной программы. К пользовательским данным такого типа относятся массивы (о них речь шла в предыдущем разделе), структуры (в других алгоритмических языках они известны под термином записи ), перечисления (в некоторых книгах по языкам C, C++ их относят к целочисленным данным) и объединения.
9.1. Структуры
Первоначальным образом для данных типа структур явились строки таблиц, с которыми знаком любой человек. Характерным для таблиц любого содержания является наличие столбцов, в каждом из которых хранятся однотипные данные. Однако в соседних столбцах типы данных могут отличаться. Если специфической особенностью массивов является использование одного и того же типа для всех элементов массива, то строки таблиц можно представлять как последовательность полей данных разного типа. Для каждого поля строки таблицы известно наименование соответствующего столбца таблицы и тип размещаемого в этом поле значения. Например, поле «Фамилия» заполняется текстовой информацией, поле «Год рождения» хранит целочисленные данные, на поле «Пол» достаточно записывать единственный символ ‘М’ или ‘Ж’ и т.д.
9.1.1. Объявление и инициализация структур
То, что принято называть «шапкой» таблицы в языках программирования носит название шаблона структуры. Например, шаблон структуры, описывающей данные о книге, может быть устроен следующим образом:
Идентификатор book выполняет функцию имени шаблона или типа структуры. В дальнейшем им можно пользоваться для объявления конкретных переменных – структур типа book :
В языке C++ служебное слово struct можно опускать:
Обратите внимание на то, что строковые поля в структурах имеют фиксированные размеры. Это существенно упрощает обработку данных, т.к. работа с полями переменной длины могла бы привести к очень медленно работающим программам.
Вообще говоря, объявление шаблона структуры и переменных, связанных с этой структурой, можно совместить:
Более того, в объявлении шаблона структуры можно опустить имя шаблона:
Однако если данная структура должна выступить в качестве параметра какой-либо функции, то и в заголовке этой функции и в ее прототипе без имени шаблона не обойтись.
Для доступа к соответствующим полям структур используются составные имена, образованные из имени структуры и имени поля:
Если мы имеем дело с указателем, который настроен на адрес структуры, то составные имена записываются с использованием двух символов «->»:
Структуры могут объединяться в массивы:
И тогда для доступа к полям соответствующего элемента используются индексированные составные имена:
При объявлении переменных типа структура их поля могут быть проинициализированы довольно естественным способом:
Для структур, объявленных с использованием одного и того же шаблона, допустима операция присваивания:
К сожалению, одноименные поля строкового типа у структур так копировать нельзя – необходимо прибегать к услугам функций типа strcpy :
Над содержимым числовых полей структур можно выполнять все операции, доступные для соответствующего типа данных.
Некоторые числовые поля структур могут быть объявлены как битовые шкалы с указанным количеством двоичных разрядов. С одной стороны, это предоставляет возможность компоновать в рамках машинных слов некоторые группы данных и располагать их более компактно в оперативной памяти. С другой стороны, такая возможность избавляет программиста от забот по выделению и вклеиванию соответствующих групп двоичных разрядов. Однако на расположение битовых полей в структурах оказывает дополнительное влияние установка компилятора – признак выравнивания данных на границу байта, слова, двойного слова. Длины битовых шкал при объявлении структур задаются следующим образом:
В зависимости от признака выравнивания границ данных эти значения могут быть расположены в оперативной памяти по-разному:
С целью принудительного расположения битовых полей в таких структурах допускается вставка безымянных полей:
Типы данных, определяемые пользователем в C++
В реальных задачах информация, которую требуется обрабатывать, может иметь достаточно сложную структуру. Для ее адекватного представления используются типы данных, построенные на основе простых типов данных, массивов и указателей. Язык C++ позволяет программисту определять свои типы данных и правила работы с ними. Исторически для таких типов сложилось наименование, вынесенные в название статьи, хотя правильнее было бы назвать их типами, определяемыми программистом.
Переименование типов (typedef)
Для того чтобы сделать программу более ясной, можно задать типу новое имя с помощью ключевого слова typedef:
typedef тип новое_имя [ размерность ];
В данном случае квадратные скобки являются элементом синтаксиса. Размерность может отсутствовать. Примеры:
typedef unsigned int UINT;
typedef char Msg[100];
typedef struct <
char f1o[30];
int date, code;
double salary;> Worker;
Введенное таким образом имя можно использовать таким же образом, как и имена стандартных типов:
UINT i, j ; // две переменных типа unsigned int
Msg str[10]; // массив из 10 строк по 100 символов
Worker staff[100]; // массив из 100 структур
Кроме задания типам с длинными описаниями более коротких псевдопимов, typedef используется для облегчения переносимости программ: если машинно-зависимые типы объявить с помощью операторов typedef, при переносе программы потребуется внести изменения только в эти операторы.
Перечисления (enum)
При написании программ часто возникает потребность определить несколько именованных констант, для которых требуется, чтобы все они имели различные значения (при этом конкретные значения могут быть не важны). Для этого удобно воспользоваться перечисляемым типом данных, все возможные значения которого задаются списком целочисленных констант. Формат:
Имя типа задается в том случае, если в программе требуется определять переменные этого типа. Компилятор обеспечивает, чтобы эти переменные принимали значения только из списка констант. Константы должны быть целочисленными и могут инициализироваться обычным образом. При отсутствии инициализатора первая константа обнуляется, а каждой следующей присваивается на единицу большее значение, чем предыдущей:
enum Err
Err error;
switch (error) <
case ERR_READ: /* операторы */ break;
case ERR_WRITE: /* операторы */ break;
case ERR_CONVERT: /* операторы */ break;
>
Константам ERR_READ, ERR_WRITE, ERR_CONVERT присваиваются значения 0, 1 и 2 соответственно.
Константам three и four присваиваются значения 3 и 4, константе eleven — 11.
Имена перечисляемых констант должны быть уникальными, а значения могут совпадать. Преимущество применения перечисления перед описанием именованных констант и директивой #define состоит в том, что связанные константы нагляднее; кроме того, компилятор при инициализации констант может выполнять проверку типов.
При выполнении арифметических операций перечисления преобразуются в целые. Поскольку перечисления являются типами, определяемыми пользователем, для них можно вводить собственные операции.
ПримечаниеДиапазон значений перечислепия определяется количеством бит, необходимым для представления всех его значений. Любое значение целочисленного типа можно явно привести к типу перечислепия, по при выходе за пределы его диапазона результат не определен.
Структуры (struct)
В отличие от массива, все элементы которого однотипны, структура может содержать элементы разных типов. В языке C++ структура является видом класса и обладает всеми его свойствами, но во многих случаях достаточно использовать структуры так, как они определены в языке С:
struct [ имя_типа ] <
тип_1 элемент_1:
тип_2 элемент_2;
тип_n элемент_n;
> [ список_описателей ];
Элементы структуры называются полями структуры и могут иметь любой тип, кроме типа этой же структуры, но могут быть указателями на него. Если отсутствует имя типа, должен быть указан список описателей переменных, указателей или массивов. В этом случае описание структуры служит определением элементов этого списка:
// Определение массива структур и указателя на структуру:
struct <
char f1o[30];
int date, code;
double salary;
>staff[100], *ps;
Если список отсутствует, описание структуры определяет новый тип, имя которого можно использовать в дальнейшем наряду со стандартными типами, например:
struct Worker < // описание нового типа Worker
char f1o[30];
int date, code;
double salary;
>; // описание заканчивается точкой с запятой
// определение массива типа Worker и указателя на тип Worker:
Worker staff[100], *ps;[/ccie_cpp]
Имя структуры можно использовать сразу после его объявления (определение можно дать позднее) в тех случаях, когда компилятору не требуется знать размер структуры, например:
struct List;. // объявление структуры List
struct Link <
List *p; // указатель на структуру List
Link *prev, *succ; // указатели на структуру Link
>;
struct List < / * определение структуры List * / >;
Это позволяет создавать связные списки структур.
Для инициализации структуры значения ее элементов перечисляют в фигурных скобках в порядке их описания:
При инициализации массивов структур следует заключать в фигурные скобки каждый элемент массива (учитывая, что многомерный массив — это массив массивов):
struct complex <
float real, im;
> compl [2][3] = <
<<1. 1>. <1. 1>. <1. 1>>. // строка 1. TO есть массив compl[0]
<<2. 2>. <2. 2>. <2. 2>> // строка 2. то есть массив compl[1]
>;
Для переменных одного и того же структурного типа определена операция присваивания, при этом происходит поэлементное копирование. Структуру можно передавать в функцию и возвращать в качестве значения функции. Другие операции со структурами могут быть определены пользователем. Размер структуры не обязательно равен сумме размеров ее
элементов, поскольку они могут быть выровнены по границам слова.
Worker worker, staff[100], *ps;
worker.fio = «Страусенке»;
staff[8].code = 215;
ps->salary = 0.12;
Если элементом структуры является другая структура, то доступ к ее элементам выполняется через две операции выбора:
Как видно из примера, поля разных структур могут иметь одинаковые имена, поскольку у них разная область видимости. Более того, можно объявлять в одной области видимости структуру и другой объект (например, переменную или массив) с одинаковыми именами, если при определении структурной переменной использовать слово struct, но не советую это делать — запутать компилятор труднее, чем себя.
Битовые поля
Битовые поля — это особый вид полей структуры. Они используются для плотной упаковки данных, например, флажков типа «да/нет». Минимальная адресуемая ячейка памяти — 1 байт, а для хранения флажка достаточно одного бита. При описании битового поля после имени через двоеточие указывается длина поля в битах (целая положительная константа):
struct Options <
bool centerX:1;
bool centerY:1;
unsigned int shadow:2;
unsigned int palette:4;
>;
Битовые поля могут быть любого целого типа. Имя поля может отсутствовать, такие поля служат для выравнивания на аппаратную границу. Доступ к полю осуществляется обычным способом — по имени. Адрес поля получить нельзя, однако в остальном битовые поля можно использовать точно так же, как обычные поля структуры. Следует учитывать, что операции с отдельными битами реализуются гораздо менее эффективно, чем с байтами и словами, так как компилятор должен генерировать специальные коды, и экономия памяти под переменные оборачивается увеличением объема кода программы. Размещение битовых полей в памяти зависит от компилятора и аппаратуры.
Объединения (union)
Объединение (union) представляет собой частный случай структуры, все поля которой располагаются по одному и тому же адресу. Формат описания такой же, как у структуры, только вместо ключевого слова struct используется слово union. Длина объединения равна наибольшей из длин его полей, В каждый момент времени в переменной типа объединение хранится только одно значение, и ответственность за его правильное использование лежит на программисте.
Объединения применяют для экономии памяти в тех случаях, когда известно, что больше одного поля одновременно не требуется:
#include
int niain() <
enum paytype
paytype ptype;
union payment <
char card[25];
long check;
> info;
/* присваивание значений info и ptype */
switch (ptype) <
case CARD: cout
Pascal: Занятие № 9. Типы данных пользователя Паскаль Type
Создание типов данных пользователя в Pascal
Типы данных в Pascal делятся на простые и сложные.
К простым типам относятся стандартные, перечисляемые и ограниченные.
К сложным типам – массивы, множества, записи, файлы. Элементами сложных типов могут быть простые и сложные типы. Мы познакомимся со сложными типами данных позже.
Одним из наиболее распространенных типов является порядковый стандартный тип.
Порядковый стандартный тип обозначает конечное линейное множество значений. К нему обычно относят целые типы, байтовые, символьные и логические.
Новые (пользовательские) типы данных нужны в первую очередь для наглядности и удобства:
const n = 10; m = 50; type matr = array [1..n, 1..m] of integer; var a : matr;
Но другой причиной является то, что в Паскале в определенных конструкциях разрешено записывать лишь имена типов, а не их определения.
Например, при описании процедур с параметрами считается неверным писать:
procedure p(a: array[1..10] of Integer);
Зато следует создать тип данных и записать так:
type vector = array[1..10] of integer; var procedure p(a: vector);
Примеры описания массивов при помощи новых типов
Три варианта описаний матрицы эквивалентны:
type vector = array[1..10] of integer; matritsa = array[1..8] of vector;
type matritsa = array[1..8] of array[1..10] of integer;
type matritsa = array[1..8,1..10] of integer;
В следующем примере переменные c и d описаны одинаково:
type vector = array[1..10] of integer; matritsa = array[1..8] of vector; var a,b: vector; c:matritsa; d:array[1..8] of vector;
Пример результата:
Перечисляемый тип и интервальный тип в Паскаль
Перечисляемый тип
В программе можно использовать переменные такого типа, который не совпадает ни с одним из стандартных типов.
Рассмотрим пример создания перечисляемого типа в Паскаль:
Интервальный тип определяет конкретное подмножество значений, которые может принимать данная переменная. Создается путем задания наименьшего и наибольшего значения порядкового типа. В качестве констант (минимального и максимального значений) могут использоваться значения любых простых типов кроме вещественных типов.
Рассмотрим пример объявления интервального типа:
Совместное использование перечисляемого и интервального типов
Переменную интервального типа можно задать с основой на базовый перечисляемый тип. Рассмотрим пример:
type color=(red,yellow,green,blue); var b:red..green; begin b:=red; writeln(b); b:=yellow; writeln(b); b:=green; writeln(b); readln end.
В данном примере тип color — является базовым. Переменная b интервального типа определена при помощи базового перечисляемого типа.
Один из вариантов решения данной задачи выглядит так:
Код данного решения обладает не самой лучшей наглядностью, к тому же приходится самому вычислять номера месяцев начала и конца лета (6 и 8).
Удобство и наглядность таких программ можно повысить следующим образом:
TYPE mes = (january, february, march, april, may, june, july, august, september, october, november, december); CONST dni:array[january..december] of Byte = (31,28,31,30,31,30,31,31,30,31,30,31); VAR s:Integer; i:mes; <переменная счетчика цикла i задана типом mes, а не Integer>BEGIN s:=0; for i:=june to august do s:=s+dni[i]; WriteLn(s) END.
Использование интервального типа в качестве диапазонного ограничителя
Следующий пример продемонстрирует неправильное решение данной задачи:
var otpravlenie,pribitie:byte; begin otpravlenie:=22; pribitie:=otpravlenie+10; writeln(pribitie) end.
программа вместо ответа «8» напечатает ответ «32».
Введение ограниченного диапазонного типа позволит избежать неправильного результата, однако компилятор все равно выдаст ошибку:
var otpravlenie,pribitie:0..24; begin otpravlenie:=22; pribitie:=otpravlenie+10; writeln(pribitie) end.
Зная об ошибке, теперь можно ее исправить.
type znak=(oven,lev,strelets,vesi,vodoley, bliznetsi,rak,skorpion,ribi,kozerog,telets,deva); var a:znak; begin a:=lev; if a =vesi) and (a =rak) and (a =kozerog) and (a