Описание структур и объединений

Структура - это объект, состоящий из последовательностиименованных членов. Каждый член может быть произвольноготипа. Объединение - это объект, который в данный моментможет содержать любой из нескольких членов. Спецификаторы иобъединения имеют одинаковую форму. спецификатор _ структуры _ или _ объединения: структура _ или _ объединение { спи - сок _ описаний _ структуры } идентификатор _ структуры _ или _ объедине - ния { список - описаний - структуры } идентификатор _ структуры _ или _ объединения структура _ или _ объединение: 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 ярлыком перечисления типа,описывающего различные цвета и затем объявляет указателемна объект этого типа, а 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. ОПЕРАТОРЫ

За исключением особо оговариваемых случаев, операторывыполняются последовательно.

Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: