Смекни!
smekni.com

деляют аргументов. Количество формальных и фактических пара-

метров должно совпадать. Текст внутри строки или символьной

константы не подлежит замене.

В обоих случаях замененная строка просматривается снова

с целью обнаружения других определенных идентификаторов. В

обоих случаях слишком длинная строка определения может быть

продолжена на другой строке, если поместить в конце продол-

жаемой строки обратную косую черту \ .

· 218 -

Описываемая возможность особенно полезна для определения

“объявляемых констант”, как, например,

#DEFINE TABSIZE 100

INT TABLE[TABSIZE];

Управляющая строка вида

#UNDEF идентификатор

приводит к отмене препроцессорного определения данного иден-

тификатора.

20.2. Включение файлов

Строка управления компилятором вида

#INCLUDE “FILENAME”

приводит к замене этой строки на все содержимое файла с име-

нем FILENAME. Файл с этим именем сначала ищется в справочни-

ке начального исходного файла, а затем в последовательности

стандартных мест. В отличие от этого управляющая строка вида

#INCLUDE <FILENAME>

ищет файл только в стандартных местах и не просматривает

справочник исходного файла.

Строки #INCLUDE могут быть вложенными.

20.3. Условная компиляция

Строка управления компилятором вида

#IF константное выражение

проверяет, отлично ли от нуля значение константного выраже-

ния (см. П. 15). Управляющая строка вида

#IF DEF идентификатор

проверяет, определен ли этот идентификатор в настоящий мо-

мент в препроцессоре, т.е. Определен ли этот идентификатор с

помощью управляющей строки #DEFINE.

21. Неявные описания

Не всегда является необходимым специфицировать и класс

памяти и тип идентификатора в описании. Во внешних определе-

ниях и описаниях формальных параметров и членов структур

класс памяти определяется по контексту. Если в находящемся

внутри функции описании не указан тип, а только класс памя-

ти, то предполагается, что идентификатор имеет тип INT; если

не указан класс памяти, а только тип, то идентификатор пред-

полагается описанным как AUTO. Исключение из последнего пра-

вила дается для функций, потому что спецификатор AUTO для

функций является бессмысленным (язык “C” не в состоянии ком-

пилировать программу в стек); если идентификатор имеет тип

“функция, возвращающая ...”, то он предполагается неявно

описанным как EXTERN.

· 219 -

Входящий в выражение и неописанный ранее идентификатор,

за которым следует скобка ( , считается описанным по контек-

сту как “функция, возвращающая INT”.

22. Снова о типах

В этом разделе обобщаются сведения об операциях, которые

можно применять только к объектам определенных типов.

22.1. Структуры и объединения

Только две вещи можно сделать со структурой или объеди-

нением: назвать один из их членов (с помощью операции) или

извлечь их адрес ( с помощью унарной операции &). Другие

операции, такие как присваивание им или из них и передача их

в качестве параметров, приводят к сообщению об ошибке. В бу-

дущем ожидается, что эти операции, но не обязательно ка-

кие-либо другие, будут разрешены.

В п. 15.1 Говорится, что при прямой или косвенной ссылке

на структуру (с помощью . Или ->) имя справа должно быть

членом структуры, названной или указанной выражением слева.

Это ограничение не навязывается строго компилятором, чтобы

дать возможность обойти правила типов. В действительности

перед '.' допускается любое L-значение и затем предполагает-

ся, что это L-значение имеет форму структуры, для которой

стоящее справа имя является членом. Таким же образом, от вы-

ражения, стоящего перед '->', требуется только быть указате-

лем или целым. В случае указателя предполагается, что он

указывает на структуру, для которой стоящее справа имя явля-

ется членом. В случае целого оно рассматривается как абсо-

лютный адрес соответствующей структуры, заданный в единицах

машинной памяти.

Такие структуры не являются переносимыми.

22.2. Функции

Только две вещи можно сделать с функцией: вызвать ее или

извлечь ее адрес. Если имя функции входит в выражение не в

позиции имени функции, соответствующей обращению к ней, то

генерируется указатель на эту функцию. Следовательно, чтобы

передать одну функцию другой, можно написать

INT F();

...

G(F);

Тогда определение функции G могло бы выглядеть так:

G(FUNCP)

INT(*FUNCP)();

&bsol;(

...

(*FUNCP)();

...

&bsol;)

Обратите внимание, что в вызывающей процедуре функция F дол-

жна быть описана явно, потому что за ее появлением в G(F) не

следует скобка ( .

· 220 -

22.3. Массивы, указатели и индексация

Каждый раз, когда идентификатор, имеющий тип массива,

появляется в выражении, он преобразуется в указатель на пер-

вый член этого массива. Из-за этого преобразования массивы

не являются L-значениями. По определению операция индексация

[] интерпретируется таким образом, что E1[E2] считается

идентичным выражению *((е1)+(е2)). Согласно правилам преоб-

разований, применяемым при операции +, если E1 - массив, а

е2 - целое, то е1[е2] ссылается на е2-й член массива е1. По-

этому несмотря на несимметричный вид операция индексации яв-

ляется коммутативной.

В случае многомерных массивов применяется последователь-

ное правило. Если е является N-мерным массивом размера

I*J*...*K, то при появлении в выражении е преобразуется в

указатель на (N-1)-мерный массив размера J*...*K. Если опе-

рация * либо явно, либо неявно, как результат индексации,

применяется к этому указателю, то результатом операции будет

указанный (N-1)-мерный массив, который сам немедленно преоб-

разуется в указатель.

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

INT X[3][5];

Здесь X массив целых размера 3*5. При появлении в выражении

X преобразуется в указатель на первый из трех массивов из 5

целых. В выражении X[I], которое эквивалентно *(X+I), снача-

ла X преобразуется в указатель так, как описано выше; затем

I преобразуется к типу X, что вызывает умножение I на длину

объекта, на который указывает указатель, а именно на 5 целых

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

адресации дает массив (из 5 целых), который в свою очередь

преобразуется в указатель на первое из этих целых. Если в

выражение входит и другой индекс, то таже самая аргументация

применяется снова; результатом на этот раз будет целое.

Из всего этого следует, что массивы в языке “C” хранятся

построчно ( последний индекс изменяется быстрее всего) и что

первый индекс в описании помогает определить общее количест-

во памяти, требуемое для хранения массива, но не играет ни-

какой другой роли в вычислениях, связанных с индексацией.

22.4. Явные преобразования указателей

Разрешаются определенные преобразования, с использовани-

ем указателей , но они имеют некоторые зависящие от конкрет-

ной реализации аспекты. Все эти преобразования задаются с

помощью операции явного преобразования типа; см. П. 15.2 и

16.7.

Указатель может быть преобразован в любой из целочислен-

ных типов, достаточно большой для его хранения. Требуется ли

при этом INT или LONG, зависит от конкретной машины. Преоб-

разующая функция также является машинно-зависимой, но она

будет вполне естественной для тех, кто знает структуру адре-

сации в машине. Детали для некоторых конкретных машин приво-

дятся ниже.

Объект целочисленного типа может быть явным образом пре-

образован в указатель. такое преобразование всегда переводит

преобразованное из указателя целое в тот же самый указатель,

но в других случаях оно будет машинно-зависимым.

· 221 -

Указатель на один тип может быть преобразован в указа-

тель на другой тип. Если преобразуемый указатель не указыва-

ет на объекты, которые подходящим образом выравнены в памя-

ти, то результирующий указатель может при использовании вы-

зывать ошибки адресации. Гарантируется, что указатель на

объект заданного размера может быть преобразован в указатель

на объект меньшего размера и снова обратно, не претерпев при

этом изменения.

Например, процедура распределения памяти могла бы прини-

мать запрос на размер выделяемого объекта в байтах, а возв-

ращать указатель на символы; это можно было бы использовать

следующим образом.

EXTERN CHAR *ALLOC();

DOUBLE *DP;

DP=(DOUBLE*) ALLOC(SIZEOF(DOUBLE));

*DP=22.0/7.0;

Функция ALLOC должна обеспечивать (машинно-зависимым спосо-

бом), что возвращаемое ею значение будет подходящим для пре-

образования в указатель на DOUBLE; в таком случае использо-

вание этой функции будет переносимым.

Представление указателя на PDP-11 соответствует 16-бито-

вому целому и измеряется в байтах. Объекты типа CHAR не име-

ют никаких ограничений на выравнивание; все остальные объек-

ты должны иметь четные адреса.

На HONEYWELL 6000 указатель соответствует 36-битовому

целому; слову соответствует 18 левых битов и два непосредст-

венно примыкающих к ним справа бита, которые выделяют символ

в слове. Таким образом, указатели на символы измеряются в

единицах 2 в степени 16 байтов; все остальное измеряется в

единицах 2 в степени 18 машинных слов. Величины типа DOUBLE

и содержащие их агрегаты должны выравниваться по четным ад-

ресам слов (0 по модулю 2 в степени 19). Эвм IBM 370 и

INTERDATA 8/32 сходны между собой. На обеих машинах адреса

измеряются в байтах; элементарные объекты должны быть выров-

нены по границе, равной их длине, так что указатели на SHORT

должны быть кратны двум, на INT и FLOAT - четырем и на

DOUBLE - восьми. Агрегаты выравниваются по самой строгой

границе, требуемой каким-либо из их элементов.

23. Константные выражения

В нескольких местах в языке “C” требуются выражения, ко-

торые после вычисления становятся константами: после вариан-