Контекстное определение типов данных в Си
В самом деле, если задана последовательность операций выделения составляющих типов данных, то по ней можно восстановить симметричную ей последовательность определения типов. Так в выражении
A.name[k]
мы несомненно имеем дело со структурированной переменной (операция "." ), элементом которой является массив name (операция [] ). Такой способ задания типа переменной в окружении (контексте) операций выделения составляющего типа будем называть контекстным определением типа. В нем могут использоваться только следующие операции:
- [] -элемент массива;
- * -косвенное обращение по указателю;
-() -вызов функции;
- () -круглые скобки, устанавливающие последовательность выполнения операций.
Заметим, что операции ("." и ->), соответствующие структуре, здесь не упоминаются. Это значит, что структурированный тип данных может быть определен только явно. Это связано с тем, что структура конструируется из разнородных элементов, а остальные типы данных строятся на основе однородных.
Контекстное определение типа понимается следующим образом. Если взять переменную некоторого неизвестного пока типа данных и выполнить над ней последовательность операций выделения составляющих типов данных, то в результате получится переменная того типа данных, который указан в левой части определения. При этом должны соблюдаться приоритеты выполнения операций, а для их изменения использоваться круглые скобки. Заданная последовательность выполнения операций дает, таким образом, обратную последовательность определения типов. Попытаемся с использованием этих правил расшифровать ряд контекстов.
int *p;
Переменная, при косвенном обращении к которой получается целое -указатель на целое.
char *p[];
Переменная, которая является массивом, при косвенном обращении к элементу которого получаем указатель на символ (строку) - массив указателей на символы (строки).
char (*p)[][80];
Переменная, при косвенном обращении к которой получается двумерный массив, состоящий из массивов по 80 символов - указатель на двумерный массив строк по 80 символов в строке.
int (*p)();
Переменная, при косвенном обращении к которой получается вызов функции, возвращающей в качестве результата целое - указатель на функцию, возвращающую целое.
int (*p[10])();
Переменная, которая является массивом, при косвенном обращении к элементу которого получается вызов функции, возвращающей целое -массив указателей на функции, возвращающих целое.
char *(*(*p)()));
Переменная, при косвенном обращении к которой получается вызов функции, при косвенном обращении к ее результату получается вызов функции, которая в качестве результата возвращает переменную, при косвенном обращении к которой получается символ -указатель на функцию, возвращающую в качестве результата указатель на функцию, возвращающую указатель на строку.
Контекстный способ определения типа является универсальным и может использоваться во всех случаях, когда речь идет о типах данных:
-определение и описание переменных;
-формальные параметры функций;
-результат функции;
-определение элементов структуры ( struct);
-определение абстрактного типа данных;
-определение типа данных (спецификатор typedef).