Операционная система UNIX. Руководство программиста

Смысл описателей


Можно трактовать всякий описатель как следующее утверждение: если конструкция, имеющая такой же вид, что и описатель, встречается в выражении, она выдает объект с указанными типом и классом памяти.

Каждый описатель содержит ровно один идентификатор; это именно тот идентификатор, который описывается. Если в качестве описателя указан просто идентификатор, он будет иметь тип, заданный спецификатором_типа в начале описания.

Идентификатор в скобках или без них в качестве описателя имеет один и тот же тип, однако скобки могут влиять на интерпретацию сложных описателей (примеры приведены ниже).

Рассмотрим описание:

T D1

где T - спецификатор_типа (такой как int и т.п.), D1 - описатель. Предположим, что это описание придает идентификатору тип "... T", где "..." пусто, если D1 является просто идентификатором (например, тип объекта x в "int x" есть int). Если же описатель D1 имеет вид

*D

то тип содержащегося в нем идентификатора - "... указатель на T".

Если описатель D1 имеет вид

D ( )

то тип содержащегося в нем идентификатора - "... функция, возвращающая T".

Если описатель D1 имеет вид

D [константное_выражение]



или

D [ ]

то тип содержащегося в нем идентификатора - "... массив T". В первом случае константное_выражение есть выражение, тип которого суть int, а значение определяется во время компиляции и должно быть положительным. (Точное определение константных_выражений дается в разделе КОНСТАНТНЫЕ ВЫРАЖЕНИЯ). Если к описателю приписано несколько спецификаций "массив ...", формируется многомерный массив; константное_выражение, специфицирующее границы массива, может быть опущено только для первой границы. Это бывает полезно, если массив является внешним и настоящее определение, для которого и выделяется память, задано где-то в другом месте. Первое константное выражение может быть опущено также, когда за описателем следует инициализатор. В таком случае размер массива вычисляется исходя из числа указанных инициализирующих элементов.


Тип элемента массива может быть одним из базовых типов, указателем, структурой либо объединением, или тоже массивом (при формировании многомерного массива).

Не все синтаксически допустимые конструкции в действительности разрешаются. Имеются следующие ограничения: функции не могут возвращать массивы и функции, однако могут возвращать указатели на них; не может быть массивов функций, однако могут быть массивы указателей на функции. Аналогично, структура или объединение не могут содержать функции, но могут содержать указатели на функции.

Например, запись

int i, *ip, f (), *fip (), (*pfi) ();

описывает i как целое, ip - как указатель на целое, f - как функцию, возвращающую целое, fip - как функцию, возвращающую указатель на целое, и pfi - как указатель на функцию, возвращающую целое. Особенно полезно сравнить два последних описателя. *fip() интерпретируется как *(fip()). Описание указывает (этого требует и конструкция такого же вида в выражении), что вызов функции fip, а затем применение к результату-указателю операции перехода к значению, дает целое. В описателе (*pfi)() (как и в соответствующем выражении) дополнительные скобки необходимы, поскольку они обозначают, что операция перехода к значению, примененная к указателю на функцию, дает функцию, которая, выполнившись, возвращает целый результат.

Еще один пример

float fa [17], *afp [17];

описывает массив типа float и массив указателей на float. Наконец, конструкция

static int x3d [3] [5] [7];

описывает статический трехмерный массив целых размера 3*5*7. Подробно: x3d - это массив из трех компонентов; каждый компонент - массив из пяти массивов; каждый из последних - массив из семи целых чисел. Любое из подвыражений x3d, x3d[i], x3d[i][j], x3d[i][j][k] может оказаться приемлемым в некотором выражении. Первые три из них имеют тип "массив", тип последнего - int.




Содержание раздела