Б. Керриган, Д. Ритчи Язык программирования C. Б. Керниган, Д. зык программирования и . Издание 3е, исправленное Перевод с английского под редакцией Вс. С. Штаркмана СанктПетербург 2003
Скачать 31.48 Mb.
|
268 Приложение А. Справочное руководство А 8. Объявления г То, каким образом интерпретируется каждый идентификатор, специ- фицируется объявлениями; они не всегда резервируют память для опи- сываемых ими идентификаторов. Объявления, резервирующие память, называются определениями и имеют следующий вид: объявление: спецификаторы-объявления Объявители в содержат объявляе- мые идентификаторы; спецификаторы-объявления представляют собой последовательности, состоящие из спецификаторов и класса памяти. спецификаторы-объявления: спецификатор-класса-памяти спецификатор-типа спецификаторы-объявления список-инициализаторов-объявителей: список-инициализаторов-объявителей , объявитель = инициализатор Объявители содержат подлежащие объявлению имена. Мы рассмотрим их позже, в А8.5. Объявление должно либо иметь по крайней мере один объявитель, либо его спецификатор типа должен определять тег структу- ры или объединения, либо - задавать элементы перечисления; пустое объявление недопустимо. Спецификаторы класса памяти Класс памяти специфицируется следующим образом: спецификатор-класса-памяти: auto register static extern Смысл классов памяти обсуждался в Спецификаторы auto и г дают объявляемым объектам класс ав- томатической памяти, и эти спецификаторы можно применять только А 8. Объявления 269 внутри функции. Объявления с auto и register одновременно являются определениями и резервируют память. Спецификатор register эквива- лентен auto, но содержит подсказку, сообщающую, что в программе объяв- ленные им объекты используются интенсивно. На регистрах может быть размещено лишь небольшое число объектов, причем определенного типа; указанные ограничения зависят от реализации. В любом случае к объекту нельзя применять (явно или неявно) унарный оператор &. Новым является правило, согласно которому вычислять адрес объекта клас- са register нельзя, а класса можно. Спецификатор дает объявляемым объектам класс статической памяти, он может использоваться и внутри, и вне функций. Внутри функ- ции этот спецификатор вызывает выделение памяти и служит определе- нием; его роль вне функций будет объяснена в Объявление со спецификатором используемое внутри функции, объявляет, что для объявляемого объекта где-то выделена память; о ее роли вне функций будет сказано в Спецификатор typedef не резервирует никакой памяти и назван спе- цификатором класса памяти из соображений стандартности синтаксиса; речь об этом спецификаторе пойдет в А8.9. Объявление может содержать не более одного спецификатора класса памяти. Если он в объявлении отсутствует, то действуют следующие пра- вила: считается, что объекты, объявляемые внутри функций, имеют класс auto; функции, объявляемые внутри - класс extern; объекты и функции, объявляемые вне функций, - статические и имеют внешние связи (см. А10, АИ). А 8.2. Спецификаторы типа Спецификаторы типа определяются следующим образом: спецификатор-типа: void char short int long float double signed unsigned структуры -или -объединения -спецификатор спецификатор-перечисления 270 Приложение А. Справочное руководство Вместе с nt допускается использование еще какого-то одного слова - или short; причем сочетание long int имеет тот же смысл, что и просто long; аналогично short int - то же самое, что и short. Слово long может употребляться вместе с double. С int и другими его модификациями (short, long или разрешается употреблять слов signed или unsigned. Любое из последних может использоваться самостоятельно, в этом слу- чае подразумевается int. Спецификатор signed бывает полезен, когда требуется обеспечить, что- бы объекты типа имели знак; его можно применять и к другим цело- численным типам, но в этих случаях он избыточен. За исключением описанных выше случаев объявление не может содер- жать более одного спецификатора типа. Если в объявлении нет ни одного спецификатора типа, то имеется в виду тип Для указания особых свойств объявляемых объектов предназначают- ся const volatile Квалификаторы типа могут употребляться с любым спецификатором типа. Разрешается инициализировать const-объект, однако присваивать ему что-либо в дальнейшем запрещается. Смысл зависит от реализации. Средства const и (изменчивый) введены стандартом ANSI. фикатор const применяется, чтобы разместить объекты в памяти, открытой только на чтение (ПЗУ), или чтобы способствовать возможной оптимиза- ции. Назначение квалификатора volatile - подавить оптимизацию, кото- рая без этого указания могла бы быть проведена. Например, в машинах, где адреса регистров ввода-вывода отображены на адресное пространство па- мяти, указатель на регистр некоторого устройства мог бы быть объявлен как volatile, чтобы запретить компилятору экономить очевидно избыточ- ную ссылку через указатель. Компилятор может игнорировать указанные квалификаторы, однако обязан сигнализировать о явных попытках изме- нить значение const-объектов. А 8.3. Объявления структур и объединений Структура - это объект, состоящий из последовательности именован- ных элементов различных типов. Объединение - объект, который в каж- дый момент времени содержит один из нескольких элементов различных типов. Объявления структур и объединений имеют один и тот же вид. спецификатор -объединения: { А 8. Объявления идентификатор -объединение: struct union является последовательностью объявле- ний элементов структуры или объединения: список-объявлений -структуры: объявление-структуры список-объявлений - структуры объявление - структуры объявление-структуры: спецификатор-типа список-структуры-объявителей: структуры-объявителъ Обычно объявление-структуры является просто объявлением для элемен- тов структуры или объединения. Элементы структуры, в свою могут состоять из заданного числа разрядов (битов). Такой элемент на- зывается битовым полем или просто полем. Его размер отделяется от име- ни поля двоеточием: структуры-объявителъ: Спецификатор типа, имеющий вид структуры-или-объединения идентификатор } объявляет идентификатор тегом структуры или объединения, специфи- цированных списком. Последующее объявление в той же или внутрен- ней области видимости может обращаться тому же типу, используя в спе- цификаторе тег без списка: структуры-или-объединения идентификатор Если спецификатор с тегом, но без списка появляется там, где тег не объявлен, специфицируется незавершенный тип. Объекты с незавершен- ным типом структуры или объединения могут упоминаться в контексте, 272 Приложение А. Справочное руководство где не требуется знать их размер — например в объявлениях (но не опре- делениях) для описания указателя или создания но не в иных случаях. Тип становится завершенным при появлении последующего спе- цификатора с этим тегом, содержащего список объявлений. Даже в спе- цификаторах со списком объявляемый тип структуры или объединения является незавершенным внутри списка и становится завершенным толь- ко после появления символа заканчивающего спецификатор. Структура не может содержать элементов незавершенного типа. Сле- довательно, невозможно объявить структуру или объединение, которые содержат сами себя. Однако, кроме придания имени типу структуры или объединения, тег позволяет определять структуры, обращающиеся сами к себе; структура или объединение могут содержать указатели на самих себя, поскольку указатели на незавершенные типы объявлять можно. Особое правило применяется к объявлениям вида идентификатор ; которые объявляют структуру или объединение, но не имеют списка объявления и объявителя. Даже если идентификатор имеет тег структу- ры или объединения во внешней области видимости это объяв- ление делает идентификатор тегом новой структуры или объединения незавершенного типа во внутренней области видимости. Это невразумительное правило - новое в ANSI. Оно предназначено для взаимно рекурсивных структур, объявленных во внутренней области ви- димости, но теги которых могут быть уже объявлены во внешней области видимости. Спецификатор структуры или объединения со списком, но без тега соз- дает уникальный к которому можно обращаться непосредственно только в объявлении, частью которого он является. Имена элементов и тегов не конфликтуют друг с другом или обыч- ными переменными. Имя элемента не может появляться дважды в одной и той же структуре или объединении, но тот же элемент можно исполь- зовать в разных структурах или объединениях. В первой редакции этой книги имена элементов структуры и объединения не связывались со своими родителями. Однако в компиляторах эта связь стала обычной задолго до появления стандарта ANSI. Элемент структуры или объединения, не являющийся полем, может иметь любой тип объекта. Поле (которое не имеет объявителя и, следова- тельно, может быть безымянным) имеет тип int, unsigned int или signed int и интерпретируется как объект целочисленного типа указанной в би- тах длины. Считается ли поле int знаковым или беззнаковым, зависит от реализации. Соседний элемент-поле упаковывается в ячейки памяти А 8. Объявления 273 в зависимости от реализации в зависящем от реализации направлении. Когда следующее за полем другое поле не влезает в частично заполнен- ную ячейку памяти, оно может оказаться разделенным между двумя ячей- ками, или ячейка может быть забита балластом. Безымянное поле нуле- вой ширины обязательно приводит к такой забивке, так что следующее поле начнется с края следующей ячейки памяти. Стандарт ANSI делает поля еще более зависимыми от реализации, чем в первой редакции книги. Чтобы хранить битовые поля в "зависящем от реализации" виде квалификации, желательно прочитать правила язы- ка. Структуры с битовыми полями могут служить переносимым способом для попытки уменьшить размеры памяти под структуру (вероятно, ценой увеличения кода программы и времени на доступ к полям) или неперено- симым способом для описания распределения памяти на битовом уровне. Во втором случае необходимо понимать правила местной реализации. Элементы структуры имеют возрастающие по мере объявления эле- ментов адреса. Элементы структуры, не являющиеся полями, выравни- ваются по границам адресов в зависимости от своего типа; таким образом, в структуре могут быть безымянные дыры. Если указатель на структуру приводится к типу указателя на ее первый элемент, результат указывает на первый элемент. Объединение можно представить себе как структуру, все элементы ко- торой начинаются со смещением 0 и размеры которой достаточны для хра- нения любого из элементов. В любой момент времени в объединении хра- нится не больше одного элемента. Если указатель на объединение приво- дится к типу указателя на один из элементов, результат указывает на этот элемент. Вот простой пример объявления структуры: struct tnode { char int count; struct tnode struct tnode }; Эта структура содержит массив из 20 символов, число типа int и два ука- зателя на подобную структуру. Если дано такое объявление, то struct tnode s, *sp; объявит s как структуру заданного вида, как указатель на такую структуру. Согласно приведенным определениям выражение sp->count обращается к элементу count в структуре, на которую указывает sp; 274 Приложение А. Справочное руководство - указатель на левое поддерево в структуре s; a s. right->tword[0] - это первый символ из - элемента правого поддерева s. Вообще говоря, невозможно проконтролировать, тот ли используется элемент объединения, которому последний раз присваивалось значение. Однако гарантируется выполнение правила, облегчающего работу с эле- ментами объединения: если объединение содержит несколько структур, начинающихся с общей для них последовательности данных, и если объе- динение в текущий момент содержит одну из этих структур, то к общей части данных разрешается обращаться через любую из указанных струк- тур. Так, правомерен следующий фрагмент программы: union { struct { int type; } n; struct { int type; int } ni; struct { int type; float floatnode; } nf; } u; = FLOAT; = if == FLOAT) A 8.4. Перечисления Перечисления - это уникальный тип, значения которого покрываются множеством именованных констант, называемых перечислителями. Вид спецификатора перечисления заимствован у структур и объединений. спецификатор-перечисления: список-перечислителей } идентификатор 8. список-перечислителей: список-перечислителей , перечислитель: идентификатор идентификатор = Идентификаторы, входящие в список перечислителей, объявляются кон- стантами типа и могут употребляться везде, где требуется константа. Если в этом списке нет ни одного перечислителя со знаком =, то значения констант начинаются с 0 и увеличиваются на 1 по мере чтения объявле- ния слева направо. Перечислитель со знаком = даёт соответствующему идентификатору значение; последующие идентификаторы продолжают прогрессию от заданного значения. Имена перечислителей, используемые в одной области видимости, дол- жны отличаться друг от друга и от имен обычных переменных, однако их значения могут и совпадать. Роль идентификатора в переч-спецификаторе аналогична роли тега структуры в структ-спецификаторе: он является именем некоторого кон- кретного перечисления. Правила для списков и переч-спецификаторов (с тегами и без) те же, что и для спецификаторов структур или объедине- ний, с той лишь оговоркой, что элементы перечислений не бывают неза- вершенного типа; тег переч-спецификатора без списка перечислителей должен иметь в пределах области видимости спецификатор со списком. В первой версии языка перечислений не было, но они уже несколько лет применяются. А 8.5. Объявители Объявители имеют следующий синтаксис: объявитель: собственно идентификатор собственно-объявителъ [ ] собственно-объявителъ ( собственно-объявителъ ) указатель: * * 276 Приложение А. Справочное руководство -типа У структуры объявителя много сходных черт со структурой подвыраже- поскольку в объявителе, как и в подвыражении, допускаются опера- ции косвенного обращения, обращения к функции и получения элемента массива (с тем же порядком применения). А 8.6. Что означают объявители Список объявителей располагается сразу после спецификаторов типа и указателя класса памяти. Главный элемент любого объявителя - это объявляемый им идентификатор; в простейшем случае объявитель из него одного и состоит, что отражено в первой строке продукции грамматики с именем Спецификаторы класса памяти отно- сятся непосредственно к идентификатору, а его тип зависит от вида объ- явителя. Объявитель следует воспринимать как утверждение: если в вы- ражении идентификатор появляется в том же контексте, что и в объяви- теле, то он обозначает объект специфицируемого типа. Если соединить спецификаторы объявления, относящиеся типу (А8.2), и некоторый конкретный объявитель, то объявление примет вид "Т где Т - тип, а - объявитель. Эта запись индуктивно придает тип идентифи- катору любого объявителя. В объявлении Т D, где D - просто идентификатор, тип идентификатора есть Т. В объявлении Т D, где D имеет вид ( ) тип идентификатора в D1 тот же, что и в D. Скобки не изменяют тип, но мо- I гут повлиять на результаты его "привязки" к идентификаторам в слож- ных объявителях. А Объявители указателей В объявления Т где D имеет вид * D1 а тип идентификатора объявления Т D1 есть Т", тип идентификатора D есть указатель Т". следующие за относятся к самому ука- зателю, а не к объекту, на который он указывает. Рассмотрим, объявление 8. Объявления 277 Здесь ар[] играет роль D1; объявление ap[] следует расшифровать (см. ниже) как "массив из список квалификаторов типа здесь пуст, а модификатор есть "массив из". Следовательно, на самом деле объяв- ление ар гласит: "массив из указателей на int". Вот еще примеры объявле- ний: int i, *pi, *const cpi = &i; const int ci = 3, *pci; В них объявляются целое i и указатель на целое pi. Значение указателя cpi неизменно; cpi всегда будет указывать в одно и то же место, даже если зна- чение, на которое он указывает, станет иным. Целое ci есть оно измениться не может (хотя может как в данном слу- чае). Тип указателя pci произносится как "указатель на const int"; сам ука- затель можно изменить; при этом он будет указывать на другое место, но значение, на которое он будет указывать, с помощью pci изменить нельзя. А 8.6.2. Объявители массивов В объявления Т D, где D имеет вид D1 [ ] и где тип идентификатора объявления Т D1 есть Т", тип идентификатора 0 есть массив из Т". Если кон- стантное выражение присутствует, то оно должно быть целочисленным и больше 0. Если константное выражение, специфицирующее количество элементов в массиве, отсутствует, то массив имеет незавершенный тип. Массив можно конструировать из объектов арифметического типа, указателей, структур и объединений, а также других массивов (генери- руя при этом многомерные массивы). Любой тип, из которого конструи- руется массив, должен быть завершенным, он не может быть, например, структурой или массивом незавершенного типа. Это значит, что для мно- гомерного массива пустой может быть только первая размерность. Неза- вершенный тип массива получает свое завершение либо в другом объяв- лении этого массива (А10.2), либо при его инициализации (А8.7). Напри- мер, запись float fa[17], объявляет массив из чисел типа и массив из указателей на числа типа Аналогично static int объявляет статический трехмерный массив целых размера 3x5x7. На са- мом деле, если быть точными, x3d является массивом из трех элементов, 278 Приложение А. Справочное руководство каждый из которых есть массив из пяти элементов, содержащих по 7 зна- чений типа int. Операция индексирования определена так, что она идентична операции *(Е1+Е2). Следовательно, несмотря на асимметричность запи- си, индексирование - коммутативная операция. Учитывая правила пре- образования, применяемые для + и массивов (А6.6, А7.1, можно сказать, что если Е1 - массив, а Е2 - целое, то Е1 [ Е2] Е2-й элемент массива Так, означает то же самое, что и ]+k). Первое подвыражение, ], согласно А7.1, приводится к типу "указатель на массив целых"; по А7.7 сложение включает умножение на размер объек- та типа int. Из этих же правил следует, что массивы запоминаются "по- строчно" (последние индексы меняются чаще) и что первая размерность в объявлении помогает определить количество памяти, занимаемой мас- сивом, однако в вычислении адреса элемента массива участия не прини- мает. А 8.6.3. Объявители функций В новом способе объявление функции Т D, где 0 имеет вид и тип идентификатора объявления Т D1 есть "модификатор-типа Т", тип идентификатора в D есть "модификатор-типа функция с аргументами список-типов-параметров, возвращающая Т". Параметры имеют следую- щий синтаксис: список-типов-параметров: список-параметров список-параметров , список-параметров: -параметра список-параметров , объявление-параметра объявление-параметра: объявитель При новом способе объявления функций список параметров специфици-' рует их типы, а если функция вообще не имеет параметров, на месте спис- ка типов указывается одно слово - void. Если список типов параметров заканчивается многоточием то функция может иметь больше ар- гументов, чем число явно описанных параметров. (См. Типы параметров, являющихся массивами и функциями, заменяются на указатели в соответствии с правилами преобразования параметров |