Структура - это объект, состоящий из последовательностиименованных членов. Каждый член может быть произвольноготипа. Объединение - это объект, который в данный моментможет содержать любой из нескольких членов. Спецификаторы иобъединения имеют одинаковую форму.
спецификатор _
структуры _
или _
объединения:
структура _
или _
объединение { спи -
сок _
описаний _
структуры } идентификатор _
структуры _
или _
объедине -
ния { список -
описаний -
структуры } идентификатор _
структуры _
или _
объединения структура _
или _
объединение:
struct union -26- Список_описаний_структуры является последовательностью опи-саний членов структуры или объединения:
список _
описаний _
структуры:
описание _
структуры описание _
структуры спи -
сок _
описаний _
структуры описание _
структуры:
спецификатор _
типа спи -
сок _
описателей _
структуры список _
описателей _
структуры:
описатель _
структуры описатель _
структуры,
список _
опи -
сателей _
структуры В обычном случае описатель структуры является просто описа-телем члена структуры или объединения. Член структуры можеттакже состоять из специфицированного числа битов. Такойчлен называется также полем; его длина отделяется от имениполя двоеточием.
описатель _
структуры:
описатель описатель:
константное _
выражение:
константное _
выражение Внутри структуры описанные в ней объекты имеют адреса, кото-рые увеличиваются в соответствии с чтением описаний объектовслева направо. Каждый член структуры, который не являетсяполем, начинается с адресной границы, соответствующей еготипу; следовательно в структуре могут оказаться неименован-ные дыры. Члены, являющиеся полями, помещаются в машинныецелые; они не перекрывают границы слова. Поле, которое неумещается в оставшемся в данном слове пространстве, помеща-ется в следующее слово. Поля выделяются справа налево наCM-ЭВМ, но могут выделяться слева направо на других машинах. Описатель структуры, который не содержит описателя, атолько двоеточие и ширину, указывает неименованное поле,полезное для заполнения свободного пространства с цельюсоответствия задаваемым извне схемам. Специальный случайнеименованного поля с шириной 0 используется для указания овыравнивании следующего поля на границу слова. При этомпредполагается, что "следующее поле" действительно являетсяполем, а не обычным членом структуры, поскольку в последнемслучае выравнивание осуществляется автоматически. Сам язык не накладывает ограничений на типы объектов,описанных как поля, но от реализаций не требуется обеспечи-вать что-либо отличное от целых полей. Более того, даже поля
-27- типа
int могут рассматриваться как не имеющие знака. На CM-ЭВМ поля не имеют знака и могут принимать только целые зна-чения. Во всех реализациях отсутствуют массивы полей и кполям не применима операция взятия адреса
&, так что несуществует и указателей на поля. Объединение можно представить себе как структуру, всечлены которой начинаются со смещения 0 и размер которой дос-таточен, чтобы содержать любой из ее членов. В каждый моментобъединение может содержать не более одного из своих членов. Спецификатор структуры или объединения во второй форме,т.е. один из:
struct идент {список _
описаний _
структуры} union идент {список -
описаний -
структуры} описывает
идент в качестве ярлыка структуры (или ярлыкаобъединения) для структуры, специфицированной этим списком.Последующее описание может затем использовать третью формуспецификатора, один из
struct идент union идент Ярлыки структур дают возможность определения структур, кото-рые ссылаются на самих себя; они также позволяют неоднок-ратно использовать приведенную только один раз длинную частьописания. Запрещается описывать структуру или объединение,которые содержат образец самого себя, но структура или объе-динение могут содержать указатель на структуру или объедине-ние такого же вида, как они сами. Имена членов и ярлыков структур могут совпадать с име-нами обычных переменных. Однако имена ярлыков и членовдолжны быть взаимно различными. Две структуры могут иметь общую начальную последова-тельность членов; это означает, что тот же самый член можетпоявиться в двух различных структурах, если он имеет одина-ковый тип в обеих структурах и если все предыдущие членыобеих структур одинаковы. Фактически компилятор только про-веряет, что имя в двух различных структурах имеет одинаковыйтип и одинаковое смещение, но если предшествующие членыотличаются, то конструкция оказывается непереносимой. Вот простой пример описания структуры:
-28- struct tnode { char tword [20];
int count;
struct tnode *left;
struct tnode *right;
}; такая структура содержит массив из 20 символов, целое и двауказателя на такие же структуры. Как только приведено такоеописание, описание
struct tnode s,
*sp; говорит о том, что
s является структурой указанного вида, а
sp является указателем на структуру указанного вида. Приналичии этих описаний выражение
sp- >
count ссылается на поле
count структуры, на которую указывает
sp;выражение
s.
left ссылается на указатель левого поддерева в структуре
s, авыражение
s.
right- >
tword [0] ссылается на первый символ члена
tword правого поддерева из
s.
Перечислимый тип
Перечислимый тип данных аналогичен скалярным типамязыка Паскаль. Спецификатор перечислимого типа имеет следу-ющий вид:
спецификатор _
перечисления:
enum список _
перечисления enum идентификатор список _
перечисления enum идентификатор список _
перечисления:
перечисляемое список _
перечисления,
перечисляемое перечисляемое:
идентификатор идентификатор = константное выражение -29- Роль идентификатора в
спецификаторе _
перечисления пол-ностью аналогична роли ярлыка структуры в
спецификаторе _
структуры; идентификатор обозначает определен-ное перечисление. Например, описание
enum color {red,
white,
black,
blue };...
enum color *cp,
col; объявляет идентификатор
color ярлыком перечисления типа,описывающего различные цвета и затем объявляет
cр указателемна объект этого типа, а
col - объектом этого типа. Идентификаторы в
списке _
перечисления становятся конс-тантами и могут появляться там, где требуются (по контексту)константы. Если не используется вторая форма перечисляемого(с равенством
=), то величины констант начинаются с 0 и воз-растают на 1 в соответствии с прочтением их описания слеванаправо. Перечисляемое с присвоением
= придает соответствую-щему идентификатору указанную величину; последующие иденти-фикаторы продолжают прогрессию от приписанной величины. Ярлыки перечислений и имена констант должны быть раз-личными и не совпадать с именами ярлыков и членов структур. Объекты данного типа перечисления рассматриваются какобъекты, имеющие тип, отличный от любых типов и контролирую-щая программа
lint сообщает об ошибках несоответствия типов.В реализации на CM_ЭВМ со всеми перечисляемыми переменнымиоперируют так, как если бы они имели тип
int.
Инициализация
Описатель может указывать начальное значение описывае-мого идентификатора. Инициализатор состоит из выражения илизаключенного в фигурные скобки списка значений, перед кото-рыми ставится знак
=.
инициализатор:
= выражение = {список_иниц} = {список _
иниц,
} список _
иниц:
выражение список _
иниц,
список _
иниц {список _
иниц} где
список _
иниц -
список _
инициализаторов -30- Все выражения, входящие в инициализатор статической иливнешней переменной, должны быть либо константными выражени-ями, либо выражениями, которые сводятся к адресу ранее опи-санной переменной, смещенному на константное (возможно,нулевое) выражение. Автоматические и регистровые переменныемогут быть инициализированы произвольными выражениями, вклю-чающими константы и ранее описанные переменные и функции. Гарантируется, что неинициализированные статические ивнешние переменные получают в качестве начальных значений 0;неинициализированные автоматические и регистровые переменныев качестве начальных значений содержат мусор. Когда инициализатор применяется к скаляру (указателюили объекту арифметического типа), то он состоит из одноговыражения, возможно заключенного в фигурные скобки. Началь-ное значение объекта находится из выражения; выполняются теже самые преобразования, что и при присваивании. Когда описываемая переменная является агрегатом (струк-турой или массивом), то инициализатор состоит из заключен-ного в фигурные скобки и разделенного запятыми списка иници-ализаторов для членов агрегата. Этот список составляется впорядке возрастания индекса или в соответствии с порядкомчленов. Если агрегат содержит подагрегаты, то это правилоприменяется рекурсивно к членам агрегата. Если количествоинициализаторов в списке оказывается меньше числа членовагрегата, то оставшиеся члены агрегата заполняются нулями.Запрещается инициализировать объединения или автоматическиеагрегаты. Фигурные скобки могут интерпретироваться следующимобразом. Если инициализатор начинается с левой фигурнойскобки, то последующий разделенный запятыми список инициали-заторов инициализирует члены агрегата; будет ошибкой, если всписке окажется больше инициализаторов, чем членов агрегата.Если однако инициализатор не начинается с левой фигурнойскобки, то из списка берется только нужное для членов дан-ного агрегата число элементов; оставшиеся элементы использу-ются для инициализации следующего члена агрегата, частьюкоторого является настоящий агрегат. Следовательно, скобки внекоторых случаях можно опускать. Последнее сокращение допускает возможность инициализа-ции массива типа
char с помощью строки. В этом случае членымассива последовательно инициализируются символами строки. Например,
int х []
= { 1,3,5
}; описывает и инициализирует
х как одномерный массив; пос-кольку размер массива не специфицирован, а список
-31- инициализатора содержит три элемента, считается, что массивсостоит из трех членов. Вот пример инициализации с полным использованием фигур-ных скобок:
float *y[4][3] = { (1, 3, 5), (2, 4, 6), (3, 5, 7), }; Здесь 1, 3 и 5 инициализируют первую строку массива
y [0], аименно
y [0][0],
y [0][1] и
y [0][2]. Аналогичным образом сле-дующие две строчки инициализируют
y [1] и
y [2]. Инициализаторзаканчивается преждевременно, и, следовательно, массив
y [3]инициализируется нулями. В точности такого же эффекта можнобыло бы достичь, написав
float y [
4 ][
3 ]
= { 1, 3, 5, 2, 4, 6, 3, 5, 7
}; Инициализатор для
y начинается с левой фигурной скобки, ноинициализатора для
y [0] нет. Поэтому используется 3 элементаиз списка. Аналогично следующие три элемента используютсяпоследовательно для
y [1] и
y [2]. Следующее описание
float y [4][3]
= { {1}, {2}, {3}, {4} }; инициализирует первый столбец
y (если его рассматривать какдвумерный массив), а остальные элементы заполняются нулями. И наконец, описание
char msg []
= "
syntax error on line %s\n "; демонстрирует инициализацию элементов символьного массива спомощью строки.
Имена типов
В двух случаях (для явного указания типа преобразованияв конструкции перевода и для аргументов операции
sizeof)желательно иметь возможность задавать тип данных. Это осу-ществляется с помощью "имени типа", которое по существуявляется описанием объекта такого типа, в котором опущеноимя самого объекта.
-32- Имя типа:
спецификатор _
типа абстрактный _
описатель абстрактный _
описатель:
пусто (
абстрактный _
описатель)
*абстрактный описатель абстрактный _
описатель ()
абстрактный _
описатель [
констант -
ное выражение ]
необ Во избежание двусмысленности в конструкции (
абстрактный _
описатель) требуется, чтобы
абстрактный _
описатель был непуст. При этомограничении возможно однозначно определить то место вабстрактном_описателе, где должен появиться идентификатор,если бы эта конструкция была описателем в описании. Имено-ванный тип совпадает тогда с типом гипотетического идентифи-катора. Например, имена типов
int int * int * [3]
int (
*)[3]
int * ()
int (
*)() именуют соответственно типы "целый", "указатель на целое","массив из трех указателей на целое", "указатель на массивиз трех целых", " функция, возвращающая указатель на целое"и "указатель на функцию, возвращающую целое".
Описатель typedef
Описания, в которых "
класс памяти " специфицирован как
typedef, не вызывают выделения памяти. Вместо этого ониопределяют идентификаторы, которые позднее можно использо-вать так, словно они являются ключевыми словами, имеющимиосновные или производные типы.
определяющее _
тип _
имя:
идентификатор В пределах области действия описания со спецификатором
typedef каждый идентификатор, описанный в нем, становитсясинтаксически эквивалентным ключевому слову, имеющему тоттип, который ассоциирует с идентификатором в описанном в п.0.4 смысле. Например, после описаний
typedef int miles,
*klicksp;
typedef struct { double re,
im;
} complex;
-33- конструкции
miles distance;
extern klicksp metricp;
complex z,
*zp; становятся законными описаниями; при этом типом distanceявляется
int, типом
metricp - "указатель на
int ", типом
z -специфицированная структура и типом
zp - указатель на такуюструктуру. Спецификатор
typedef не вводит каких-либо совершенноновых типов, а только определяет синонимы для типов, которыеможно было бы специфицировать и другим способом. Так в при-веденном выше примере переменная
distance считается имеющейточно такой же тип, что и любой другой объект, описанный в
int.
* 6. ОПЕРАТОРЫ
За исключением особо оговариваемых случаев, операторывыполняются последовательно.