8.7  Интерпретация составных описателей

При написании программ, использующих указатели, часто используются описатели. Одним из способов создания простых описателей является применение ключевого слова typedef.

Описания, специфицированные ключевым словом  typedef не вызывают выделения памяти. Вместо этого они определяют идентификаторы, которые можно использовать так, словно они являются ключевыми словами. Например, идентификаторы  

typedef int miles;

typedef struct {double re,im;} complex;

можно использовать для создания объектов. Например:

miles distance;  // объект имеет тип int

complex z;        //  объект имеет тип структуры complex

Каждый описатель содержит только один описываемый идентификатор.

Описатели могут быть простые и составные. Составной описатель – это  идентификатор, дополненный более чем одним признаком типа массив, функция или указатель. С одним идентификатором составного описателя можно образовывать  множество различных комбинаций признаков типа массив, указатель или функция.

При интерпретации составных описателей сначала рассматриваются квадратные и круглые скобки, расположенные справа от идентификатора. Квадратные и круглые скобки имеют одинаковый приоритет. Они интерпретируются слева направо. После них справа налево рассматриваются звездочки, расположенные слева от идентификатора. Специ­фикация типа рассматривается на последнем шаге после того, как описатель уже полностью проинтерпретирован. Для изменения действующего по умолчанию порядка ин­терпретации описателя можно использовать внутри него круг­лые скобки.

Следующий пример иллюстрирует применение правила интерпретации составных описателей:

char*(*(*var)())[10];

7  6 4 2  1   3    5

Здесь последовательность ша­гов интерпретации пронумерована:

1) идентификатор var объявлен как

2) указатель на

3) функцию, не имеющую аргументов и возвращающую

4) указатель на

5) массив из 10 элементов, каждый из которых является

6) указателем на

7) значения типа char.

Рассмотрим примеры, которые демонстрируют, как применение круглых скобок может изменять смысл объяв­лений:

1) int *var[5]; – идентификатор var является именем массива из пяти элементов, каждый элемент которого является указателем на тип int;

2) int (*var) [5]; – идентификатор var является указателем на массив из пяти элементов, каждый элемент которого имеет тип int;

3) long *var(); – идентификатор var является именем функции, не имеющей аргументов и возвращающей указатель на тип long;

4) long (*var)(); – идентификатор var является указателем на функцию, не имеющую аргументов и возвращающую значение типа long;

5) struct both

{

int a;

char b;

} (*var[5])(); – идентификатор var является именем массива из пяти элементов,  каждый элемент которого является указателем на функцию, не имеющую аргументов и возвращающую значения типа структуры both.

6) double (*var(int)) [3]; – идентификатор var является именем функции c аргументом типа int, возвращающей указатель на массив из трех элементов, каждый элемент которого имеет тип double;

7) union sign

{

int x;

unsigned y;

} **var[5][5]; – идентификатор var является именем матрицы 5 х 5, каждый элемент которой является указа­телем на указатель на объединение типа sign;

8) union sign *(*var[5]) [5]; – идентификатор var является именем массива из пяти элементов, каждый элемент массива является указателем на безымянный массив из пяти элементов, каждый элемент безымянного массива является указателем на объединение  типа sign.