Иерархия и конструирование типов данных
В Си используется общепринятый принцип иерархического конструирования типов данных. Имеется набор базовых типов данных, операции над которыми включены в язык программирования. Производные типы данных конструируются в программе из уже известных, в том числе базовых типов данных. Понятно, что в языке программирования отсутствуют операции для работы с производным типом данных в целом. Но для каждого способа его определения существует операция ВЫДЕЛЕНИЯ СОСТАВЛЯЮЩЕГО ТИПА ДАННЫХ.
.
ПРОИЗВОДНЫЙ ТИП ДАННЫХ ОПЕРАЦИЯ ВЫДЕЛЕНИЯ СОСТАВЛЯЮЩЕГО ТИПА ДАННЫХ
Массив [] - элемент массива
Указатель * - косвенное обращение по указателю
Структура(объединение) . - элемент структуры
Указатель на структуру -> - элемент структуры, адресуемой указателем
Функция () - вызов функции
Теперь рассмотрим известные нам операции с точки зрения взаимоотношения типов данных и переменных.
struct man A;
int n;
n = A.dd;
В программе определяется переменная производного типа данных -структурированная переменная A. Ее имя идентифицирует всю переменную в целом -область памяти с именем А, которой соответствует тип данных struct man . Операция "точка" в выражении A.dd выделяет в переменной A компоненту, которая соответствует составляющему типу данных int dd . Если рассматривать эту операция только с позиции преобразования типов данных, то в ней осуществляется переход от производного типа данных переменной А к типу данных составляющей ее компоненты -int .
Если эту схему представить в общем виде, то мы увидим, какие существуют взаимоотношения между типами данных, переменными и операциями выделения составляющих типов данных.
.
БТД --- тип выражения -----op2(op1(X))
| |
определение ПТД выделение составляющего ТД
| |
ПТД1 -- тип выражения --------- op1(X)
| |
определение ПТД выделение составляющего ТД
| |
ПТД2 --------------------------- X
определение переменной
Прежде всего, в программе создается цепочка определений производных типов данных: базовый тип данных (БТД) используется для определения производного (ПТД1), который в свою очередь используется для определения другого производного типа данных (ПТД2) и т.д..
Затем определяется переменная (X), которая относится к одному из типов данных в этой цепочке (ПТД2). Под нее выделяется область памяти, которая получает общее имя (X). К этому имени могут быть применены операции выделения составляющих типов данных (op1 и op2), они осуществляют переход к внутренним компонентам составляющих переменную типов данных. Операции эти должны применяться в обратном порядке по отношению к последовательности определения типов данных. Типы полученных выражений также повторяют в обратном порядке эту последовательность.
Посмотрим, как укладывается в предложенную схему пример с пресловутой struct man:
.
struct man B[20];
char c;
c = B[i].name[j];
БТД cha r B[i].name[j]
символ | |
| | операция []
ПТД1 char[20]; B[i].name
массив символов | |
| | операция "."
ПТД2 struct ma n B[i]
структура { char name[20];...}; |
| |операция []
ПТД3 struct man B[10]; --- B
массив структур
Базовый тип char (БТД) используется для создания производного типа -массив из 20 символов (ПТД1). Тип данных -структура (ПТД2) использует массив символов в качестве одного из составляющих ее элементов. Последний тип данных -массив из 10 структур (ПТД3) порождает переменную B соответствующего типа. Затем все происходит в обратном порядке. Операции [],"." и [] последовательно выделяют в переменной B i-ю структуру, элемент структуры name и j-й символ в этом элементе.
Если внимательно посмотреть на схему, то можно заметить, что в программе в явном виде упоминаются только два типа данных -базовый char и структура struct man . Остальные два типа -массив символов и массив структур -отсутствуют. Эти типы данных создаются "на ходу", в процессе определения переменной B и элемента структуры name . Следовательно, в Си существует некоторый способ, который позволяет наряду с определением переменной задать и ее тип. На самом деле он является основным и используется при работе с массивами, указателями и функциями.