ии набора символов. Некоторые неграфические символы, одиночная кавычка ' и обратная косая черта \ могут быть представлены в соответст- вии со следующей таблицей условных последовательностей: новая строка NL/LF/ \N горизонтальная табуляция HT \T символ возврата на одну позицию BS \B возврат каретки CR \R переход на новую страницу FF \F обратная косая черта \ \\ одиночная кавычка ' \' комбинация битов DDD \DDD Условная последовательность \DDD состоит из обратной ко- сой черты, за которой следуют 1,2 или 3 восмеричных цифры, которые рассмативаются как задающие значение желаемого сим- вола. Специальным случаем этой конструкции является последо- вательность \0 (за нулем не следует цифра), которая опреде- ляет символ NUL. если следующий за обратной косой чертой символ не совпадает с одним из указанных, то обратная косая черта игнорируется. 10.4.4. Плавающие константы Плавающая константа состоит из целой части, десятичной точки, дробной части, буквы E (маленькая) или E (большая) и целой экспоненты с необязательным знаком. Как целая, так и дробная часть являются последовательностью цифр. Либо целая, либо дробная часть (но не обе) может отсутствовать; либо де- сятичная точка, либо е (маленькая) и экспонента (но не то и другое одновременно) может отсутствовать. Каждая плавающая константа считается имеющей двойную точность. 10.5. Строки Строка - это последовательность символов, заключенная в двойные кавычки, как, наприимер,"...". Строка имеет тип "массив массивов" и класс памяти STATIC (см. Пункт 4 ниже). Строка инициализирована указанными в ней символами. Все строки, даже идентично записанные, считаются различными. Компилятор помещает в конец каждой строки нулевой байт \0, с тем чтобы просматривающая строку программа могла определить ее конец. Перед стоящим внутри строки символом двойной ка- вычки " должен быть поставлен символ обратной косой черты \; кроме того, могут использоваться те же условия последова- тельности, что и в символьных константах. И последнее, об- ратная косая черта \, за которой непосредственно следует символ новой строки, игнорируется. 10.6. Характеристики аппаратных средств Следующая ниже таблица суммирует некоторые свойства ап- паратного оборудования, которые меняются от машины к машине. Хотя они и влияют на переносимость программ, на практике они представляют маленькую проблему, чем это может казаться за- ранее. Таблица 1 ------------------------------------------------------- DEC PDP-11 HONEYWELL IBM 370 INTERDATA 8/32 ASCII ASCII EBCDIC ASCII CHAR 8 BITS 9 BITS 8 BITS 8 BITS INT 16 36 32 32 SHORT 16 36 16 16 LONG 32 36 32 32 FLOAT 32 36 32 32 DOUBLE 64 72 64 64 RANGE -38/+38 -38/+38 -76/+76 -76/+76 -------------------------------------------------------- 11. Синтаксическая нотация В используемой в этом руководстве синтаксической нотации синтаксические категории выделяются курсивом (прим. перев.: в настоящее время синтексические категории вместо курсивом выделяются подчеркиванием), а литерные слова и символы - жирным шрифтом. Альтернативные категории перечисляются на отдельных строчках. Необязательный символ, терминальный или нетерминальный, указывается индексом "необ", так что \( выражение --------- необ \) указывает на необязательное выражение, заключенное в фигур- ных скобках. Синтаксис суммируется в пункте 18. 12. Что в имени тебе моем? Язык "C" основывает интерпретацию идентификатора на двух признаках идентификатора: его классе памяти и его типе. Класс памяти определяет место и время хранения памяти, свя- занной с идентификатором; тип определяет смысл величин, на- ходящихся в памяти, определенной под идентификатором. Имеются четыре класса памяти: автоматическая, статичес- кая, внешняя и регистровая. Автоматические переменные явля- ются локальными для каждого вызова блока и исчезают при вы- ходе из этого блока. Статические переменные являются локаль- ными, но сохраняют свои значения для следующего входа в блок даже после того, как управление передается за пределы блока. Внешние переменные существуют и сохраняют свои значения в течение выполнения всей программы и могут использоваться для связи между функциями, в том числе и между независимо ском- пилированными функциями. Регистровые переменные хранятся (ели это возможно) в быстрых регистрах машины; подобно авто- матическим переменным они являются локальными для каждого блока и исчезают при выходе из этого блока. В языке "C" предусмотрено несколько основных типов объектов: объекты, написанные как символы (CHAR), достаточно вели- ки, чтобы хранить любой член из соответствующего данной реа- лизации внутреннего набора символов, и если действительный символ из этого набора символов хранится в символьной пере- менной, то ее значение эквивалентно целому коду этого симво- ла. В символьных переменных можно хранить и другие величины, но реализация будет машинно-зависимой. Можно использовать до трех размеров целых, описываемых как SHORT INT, INT и LONG INT. Длинные целые занимают не меньше памяти, чем короткие, но в конкретной реализации мо- жет оказаться, что либо короткие целые, либо длинные целые, либо те и другие будут эквивалентны простым целым. "Простые" целые имеют естественный размер, предусматриваемый архиитек- турой используемой машины; другие размеры вводятся для удво- летворения специальных потребностей. Целые без знака, описываемые как UNSIGNED, подчиняются законам арифметики по модулю 2**N, где N - число битов в их представлении. (На PDP-11 длинные величины без знака не пре- дусмотрены). Плавающие одинарной точности (FLOAT) и плавающие двойной точности (DOUBLE) в некоторых реализациях могут быть синони- мами. Поскольку объекты упомянутых выше типов могут быть ра- зумно интерпретированы как числа, эти типы будут называться арифметическими. типы CHAR и INT всех размеров совместно бу- дут называться целочисленными. Типы FLOAT и DOUBLE совместно будут называться плавающими типами. Кроме основных арифметических типов существует концепту- ально бесконечный класс производных типов, которые образуют- ся из основных типов следующим образом: массивы объектов большинства типов; функции, которые возвращают объекты заданного типа; указатели на объекты данного типа; структуры, содержащие последовательность объектов различных типов; объединения, способные содержать один из нескольких объектов различных типов. Вообще говоря, эти методы построения объектов могут при- меняться рекурсивно. 13. Объекты и L-значения Объект является доступным обработке участком памяти; L-значение - это выражение, ссылающееся на объект. Очевидным примером выражения L-значения является идентификатор. Сущес- твуют операции, результатом которых являются L-значения; ес- ли, например, E - выражение указанного типа, то *E является выражением L-значения, ссылающимся на объект E. Название "L-значение" происходит от выражения присваивания E1=E2, в котором левая часть должна быть выражением L-значения. При последующем обсуждении каждой операции будет указываться, ожидает ли она операндов L-значения и выдает ли она L-значе- ние. 14. Преобразования Ряд операций может в зависимости от своих операндов вы- зывать преобразование значение операнда из одного типа в другой. В этом разделе объясняются результаты, которые сле- дует ожидать от таких преобразований. В п. 14.6 Подводятся итоги преобразований, требуемые большинством обычных опера- ций; эти сведения дополняются необходимым образом при обсуж- дении каждой операции. 14.1. Символы и целые Символ или короткое целое можно использовать всюду, где можно использовать целое. Во всех случаях значение преобра- зуется к целому. Преобразование более короткого целого к бо- лее длинному всегда сопровождается знаковым расширением; це- лые являются величинами со знаком. Осуществляется или нет знаковое расширение для символов, зависит от используемой машины, но гарантируется, что член стандартного набора сим- волов неотрицателен. из всех машин, рассматриваемых в этом руководстве, только PDP-11 осуществляет знаковое расширение. область значений символьных переменных на PDP-11 меняется от -128 до 127; символы из набора ASC11 имеют положительные значения. Символьная константа, заданная с помощью восьме- ричной условной последовательности, подвергается знаковому расширению и может оказаться отрицательной; например, '\377' имеет значение -1. Когда более длинное целое преобразуется в более короткое или в CHAR, оно обрезается слева; лишние биты просто отбра- сываются. 14.2. Типы FLOAT и DOUBLE Вся плавающая арифметика в "C" выполняется с двойной точностью каждый раз, когда объект типа FLOAT появляется в выражении, он удлиняется до DOUBLE посредством добавления нулей в его дробную часть. когда объект типа DOUBLE должен быть преобразован к типу FLOAT, например, при присваивании, перед усечением DOUBLE округляется до длины FLOAT. 14.3. Плавающие и целочисленные величины Преобразование плавающих значений к целочисленному типу имеет тенденцию быть до некоторой степени машинно-зависимым; в частности направление усечения отрицательных чисел меняет- ся от машине к машине. Результат не определен, если значение не помещается в предоставляемое пространство. Преобразование целочисленных значений в плавающие выпол- няется без осложнений. Может произойти некоторая потеря точ- ности, если для результата не содержится достаточного коли- чества битов. 14.4. Указатели и целые Целое или длинное целое может быть прибавлено к указате- лю или вычтено из него; в этом случае первая величина преоб- разуется так, как указывается в разделе описания операции сложения. Два указателя на объекты одинакового типа могут быть вычтены; в этом случае результат преобразуется к целому, как указывается в разделе описания операции вычитания. 14.5. Целое без знака Всякий раз, когда целое без знака объединяется с простым целым, простое целое преобразуется в целое без знака и ре- зультат оказывается целым без знака. Значением является наи- меньшее целое без знака, соответствующее целому со знаком (по модулю 2**размер слова). В двоичном дополнительном пред- ставлении это преобразование является чисто умозрительным и не изменяет фактическую комбинацию битов. Когда целое без знака преобразуется к типу LONG, значе- ние результата совпадает со значением целого без знака. Та- ким образом, это преобразование сводится к добавлению нулей слева. 14.6. Арифметические преобразования Подавляющее большинство операций вызывает преобразование и определяет типы результата аналогичным образом. Приводимая ниже схема в дальнейшем будет называться "обычными арифмети- ческими преобразованиями". Сначала любые операнды типа CHAR или SHORT преобразуются в INT, а любые операнды типа FLOAT преобразуются в DOUBLE. Затем, если какой-либо операнд имеет тип DOUBLE, то другой преобразуется к типу DOUBLE, и это будет типом результата. В противном случае, если какой-либо операнд имеет тип LONG, то другой операнд преобразуется к типу LONG, и это и будет типом результата. В противном случае, если какой-либо операнд имеет тип UNSIGNED, то другой операнд преобразуется к типу UNSIGNED, и это будет типом результата. В противном случае оба операнда будут иметь тип INT, и это будет типом результата. 15. Выражения Старшинство операций в выражениях совпадает с порядком следования основных подразделов настоящего раздела, начиная с самого высокого уровня старшинства. Так, например, выраже- ниями, указываемыми в качестве операндов операции + (п.15.4), Являются выражения, определенные в п.п.15.1-15.3. Внутри каждого подраздела операции имеет одинаковое старшин- ство. В каждом подразделе для описываемых там операций ука- зывается их ассоциативность слева или справа. Старшинство и ассоциативность всех операций в выражениях резюмируются в грамматической сводке в п.18. В противном случае порядок вычислений выражений не опре- делен. В частности, компилятор считает себя в праве вычис- лять подвыражения в том порядке, который он находит наиболее эффективным, даже если эти подвыражения приводят к побочным эффектам. Порядок, в котором происходят побочные эффекты, не специфицируется. Выражения, включающие коммутативные и ассо- циативные операции ( *,+,&,!,^ ), могут быть переупорядочены произвольным образом даже при наличии круглых скобок; чтобы вынудить определенный порядок вычислений, в этом случае не- обходимо использовать явные промежуточные переменные. При вычислении выражений обработка переполнения и про- верка при делении являются машинно-зависимыми. Все существу- ющие реализации языка "C" игнорируют переполнение целых; об- работка ситуаций при делении на 0 и при всех особых случаях с плавающими числами меняется от машины к машине и обычно выполняется с помощью библиотечной функции. 15.1. Первичные выражения Первичные выражения, включающие ., ->, индексацию и об- ращения к функциям, группируются слева направо. Первичное выражение: идентификатор константа строка (выражение) первичное-выражение [выражение] первичное-выражение (список-выражений нео первичное-L-значение . Идентификатор первичное-выражение -> идентификатор список-выражений: выражение список-выражений, выражение Идентификатор является первичным выражением при условии, что он описан подходящим образом, как это обсуждается ниже. тип идентификатора определяется его описанием. Если, однако, ти- пом идентификатора является "массив ...", то значением выра- жения, состоящего из этого идентификатора , является указа- тель на первый объект в этом массиве, а типом выражения бу- дет "указатель на ...". Более того, идентификатор массива не является выражением L-значения. подобным образом идентифика- тор, который описан как "функция, возвращающая ...", за иск- лючением того случая, когда он используется в позиции имени функции при обращении, преобразуется в "указатель на функ- цию, которая возвращает ...". Константа является первичным выражением. В зависимости от ее формы типом константы может быть INT, LONG или DOUBLE. Строка является первичным выражением. Исходным ее типом является "массив символов"; но следуя тем же самым правилам, которые приведены выше для идентификаторов, он модифицирует- ся в "указатель на символы", и результатом является указа- тель на первый символ строки. (имеется исключение в некото- рых инициализаторах; см. П. 16.6.) Выражение в круглых скобках является первичным выражени- ем, тип и значение которого идентичны типу и значению этого выражения без скобок. Наличие круглых скобок не влияет на то, является ли выражение L-значением или нет. Первичное выражение, за которым следует выражение в квадратных скобках, является первичным выражением. Интуитив- но ясно, что это выражение с индексом. Обычно первичное вы- ражение имеет тип "указатель на ...", индексное выражение имеет тип INT, а типом результата является "...". Выражение E1[E2] по определению идентично выражению * ((E1) + (E2)). Все, что необходимо для понимания этой записи, содержится в этом разделе; вопросы, связанные с понятием идентификаторов и операций * и + рассматриваются в п.п. 15.1, 15.2 И 15.4 соответственно; выводы суммируются ниже в п. 22.3. Обращение к функции является первичным выражением, за которым следует заключенный в круглые скобки возможно пустой список выражений, разделенных запятыми, которые и представ- ляют собой фактические аргументы функции. Первичное выраже- ние должно быть типа "функция, возвращающая ...", а резуль- тат обращения к функции имеет тип "...". Как указывается ни- же, ранее не встречавщийся идентификатор, за которым непос- редственно следует левая круглая скобка, считается описанным по контексту, как представляющий функцию, возвращающую це- лое; следовательно чаще всего встречающийся случай функции, возвращающей целое значение, не нуждается в описании. Перед обращением любые фактические аргументы типа FLOAT преобразуются к типу DOUBLE, любые аргументы типа CHAR или SHORT преобразуются к типу INT, и, как обычно, имена масси- вов преобразуются в указатели. Никакие другие преобразования не выполняются автоматически; в частности, не сравнивает ти- пы фактических аргументов с типами формальных аргументов. Если преобразование необходимо, используйте явный перевод типа (CAST); см. П.п. 15.2, 16.7. При подготовке к вызову функции делается копия каждого фактического параметра; таким образом, все передачи аргумен- тов в языке "C" осуществляются строго по значению. функция может изменять значения своих формальных параметров, но эти изменения не влияют на значения фактических параметров. С другой строны имеется возможность передавать указатель при таком условии, что функция может изменять значение объекта, на который этот указатель указывает. Порядок вычисления ар- гументов в языке не определен; обратите внимание на то, что различные компиляторы вычисляют по разному. Допускаются рекурсивные обращения к любой функции. Первичное выражение, за которым следует точка и иденти- фикатор, является выражением. Первое выражение должно быть L-значением, именующим структуру или объединение, а иденти- фикатор должен быть именем члена структуры или объединения. Результатом является L-значение, ссылающееся на поименован- ный член структуры или объединения. Первичное выражение, за которым следует стрелка (состав- ленная из знаков - и >) и идентификатор, является выражени- ем. первое выражение должно быть указателем на структуру или объединение, а идентификатор должен именовать член этой структуры или объединения. Результатом является L-значение, ссылающееся на поименованный член структуры или объединения, на который указывает указательное выражение. Следовательно, выражение E1->MOS является тем же самым, что и выражение (*E1).MOS. Структуры и объединения рассмат- риваются в п. 16.5. Приведенные здесь правила использования структур и объединений не навязываются строго, для того что- бы иметь возможность обойти механизм типов. См. П. 22.1. 15.2. Унарные операции Выражение с унарными операциями группируется справо на- лево. Унарное-выражение: * выражение & L-значение - выражение ! Выражение \^ выражение ++ L-значение -- L-значение L-значение ++ L-значение -- (имя-типа) выражение SIZEOF выражение SIZEOF имя-типа Унарная операция * означает косвенную адресацию: выраже- ние должно быть указателем, а результатом является L-значе- ние, ссылающееся на тот объект, на который указывает выраже- ние. Если типом выражения является "указатель на...", то ти- пом результата будет "...". Результатом унарной операции & является указатель на объект, к которому ссылается L-значение. Если L-значение имеет тип "...", то типом результата будет "указатель на ...". Результатом унарной операции - (минус) является ее опе- ранд, взятый с противоположным знаком. Для величины типа UNSIGNED результат получается вычитанием ее значения из 2**N (два в степени N), где N-число битов в INT. Унарной операции + (плюс) не существует. Результатом операции логического отрицания ! Является 1, если значение ее операнда равно 0, и 0, если значение ее операнда отлично от нуля. Результат имеет тип INT. Эта опе- рация применима к любому арифметическому типу или указате- лям. Операция \^ дает обратный код, или дополнение до едини- цы, своего операнда. Выполняются обычные арифметические пре- образования. Операнд должен быть целочисленного типа. Объект, на который ссылается операнд L-значения префикс- ной операции ++, увеличивается. значением является новое значение операнда, но это не L-значение. Выражение ++х экви- валентно х+=1. Информацию о преобразованиях смотри в разборе операции сложения (п. 15.4) и операции присваивания (п. 15.14). Префиксная операция -- аналогична префиксной операции ++, но приводит к уменьшению своего операнда L-значения. При применении постфиксной операции ++ к L-значению ре- зультатом является значение объекта, на который ссылается L-значение. После того, как результат принят к сведению, объект увеличивается точно таким же образом, как и в случае префиксной операции ++. Результат имеет тот же тип, что и выражение L-значения. При применении постфиксной операции -- к L-значению ре- зультатом является значение объекта, на который ссылается L-значение. После того, как результат принят к сведению, объект уменьшается точно таким же образом, как и в случае префиксной операции --. Результат имеет тот же тип, что и выражение L-значения. Заключенное в круглые скобки имя типа данных,стоящее пе- ред выражением , вызывает преобразование значения этого вы- ражения к указанному типу. Эта конструкция называется пере- вод (CAST). Имена типов описываются в п. 16.7. Операция SIZEOF выдает размер своего операнда в байтах. (Понятие байт в языке не определено, разве только как значе- ние операции SIZEOF. Однако во всех существующих реализациях байтом является пространство, необходимое для хранения объекта типа CHAR). При применении к массиву результатом яв- ляется полное число байтов в массиве. Размер определяется из описаний объектов в выражении. Это выражение семантически является целой константой и может быть использовано в любом месте, где требуется константа. Основное применение эта опе- рация находит при связях с процедурами, подобным распредели- телям памяти, и в системах ввода- вывода. Операция SIZEOF может быть также применена и к заключен- ному в круглые скобки имени типа. В этом случае она выдает размер в байтах объекта указанного типа. Конструкция SIZEOF (тип) рассматривается как целое, так что выражение SIZEOF (тип) - 2 эквивалентно выражению (SIZEOF (тип)9 - 2. 15.3. Мультипликативные операции Мультипликативные операции *, /, и % группируются слева направо. Выполняются обычные арифметические преобразования. Мультипликативное-выражение: выражение * выражение выражение / выражение выражение % выражение Бинарная операция * означает умножение. Операция * ассо- циативна, и выражения с несколькими умножениями на одном и том же уровне могут быть перегруппированы компилятором. Бинарная операция / означает деление. При делении поло- жительных целых осуществляется усечение по направлению к ну- лю, но если один из операндов отрицателен, то форма усечения зависит от используемой машины. На всех машинах, охватывае- мых настоящим руководством, остаток имеет тот же знак , что и делимое. Всегда справедливо, что (A/B)*B+A%B равно A (если B не равно 0). Бинарная операция % выдает остаток от деления первого выражения на второе. Выполняются обычные арифметические пре- образования. Операнды не должны быть типа FLOAT. 15.4. Аддитивные операции Аддитивные операции + и - группируются слева направо. выполняются обычные арифметические преобразования. Для каж- дой операции имеются некоторые дополнительные возможности, связанные с типами операндов. Аддитивное-выражение: выражение + выражение выражение - выражение Результатом операции + является сумма операндов. Можно скла- дывать указатель на объект в массиве и значение любого цело- численного типа. во всех случаях последнее преобразуется в адресное смещение посредством умножения его на длину объек- та, на который указывает этот указатель. Результатом являет- ся указатель того же самого типа, что и исходный указатель, который указывает на другой объект в том же массиве, смещен- ный соответствующим образом относительно первоначального объекта. Таким образом, если P является указателем объекта в массиве, то выражение P+1 является указателем на следующий объект в этом массиве. Никакие другие комбинации типов для указателей не разре- шаются. Операция + ассоциативна, и выражение с несколькими сло- жениями на том же самом уровне могут быть переупорядочены компилятором. Результатом операции - является разность операндов. Вы- полняются обычные арифметические преобразования. Кроме того, из указателя может быть вычтено значение любого целочислен- ного типа, причем, проводятся те же самые преобразования, что и при операции сложения. Если вычитаются два указателя на объекты одинакового ти- па, то результат преобразуется (делением на длину объекта) к типу INT, представляя собой число объектов, разделяющих ука- зываемые объекты. Если эти указатели не на объекты из одного и того же массива, то такое преобразование, вообще говоря, даст неожиданные результаты, потому что даже указатели на объекты одинакового типа не обязаны отличаться на величину, кратную длине объекта. 15.5. Операции сдвига Операции сдвига << и >> группируются слева направо. Для обеих операций проводятся обычные арифметические преобразо- вания их операндов, каждый из которых должен быть целочис- ленного типа. Затем правый операнд преобразуется к типу INT; результат имеет тип левого операнда. Результат не определен, если правый операнд отрицателен или больше или равен, чем длина объекта в битах. Выражение-сдвига: выражение << выражение выражение >> выражение Значением выражения E1<<E2 является E1 (интерпретируемое как комбинация битов), сдвинутое влево на E2 битов; освобождаю- щиеся биты заполняются нулем. значением выражения E1>>E2 яв- ляется E1, сдвинутое вправо на E2 битовых позиций. Если E1 имеет тип UNSIGNE, то сдвиг вправо гарантированно будет ло- гическим (заполнение нулем); в противном случае сдвиг может быть (и так и есть на PDP-11) арифметическим (освобождающие- ся биты заполняются копией знакового бита). 15.6. Операции отношения Операции отношения группируются слева направо, но этот факт не очень полезен; выражение A<B<C не означает того, что оно казалось бы должно означать. Выражение-отношения: выражение < выражение выражение > выражение выражение <= выражение выражение >= выражение Операции < (меньше), > (больше), <= (меньше или равно) и >= (больше или равно) все дают 0, если указанное отношение лож- но, и 1, если оно истинно. Результат имеет тип ITN. Выполня- ются обычные арифметические преобразования. Могут сравни- ваться два указателя; результат зависит от относительного расположения указываемых объектов в адресном пространстве. Сравнение указателей переносимо только в том случае, если указатели указывают на объекты из одного и того же массива. 15.7. Операции равенства Выражение-равенства: выражение == выражение выражение != выражение Операции == (равно) и != (не равно) в точности аналогичны операциям отношения, за исключением того, что они имеют бо- лее низкий уровень старшинства. (Поэтому значение выражения A<B==C<D равно 1 всякий раз, когда выражение A<B и C<D имеют одинаковое значение истинности). Указатель можно сравнивать с целым, но результат будет машинно- независимым только в том случае, если целым являет- ся константа 0. Гарантируется, что указатель, которому прис- воено значение 0, не указывает ни на какой объект и на самом деле оказывается равным 0; общепринято считать такой указа- тель нулем. 15.8. Побитовая операция 'и' Выражение-и: выражение & выражение Операция & является ассоциативной, и включающие & выражения могут быть переупорядочены. Выполняются обычные арифметичес- кие преобразования; результатом является побитовая функция 'и' операндов. Эта операция применима только к операндам це- лочисленного типа. 15.9. Побитовая операция исключающего 'или' Выражение-исключающего-или: выражение ^ выражение Операция ^ является ассоциативной, и включающие ^ выражения могут быть переупорядочены. выполняются обычные арифметичес- кие преобразования; результатом является побитовая функция исключающего 'или' операндов. Операция применима только к операндам целочисленного типа. 15.10. Побитовая операция включающего 'или' Выражение-включающего-или: выражение \! Выражение Операция \! Является ассоциативной, и содержащие \! Выраже- ния могут быть переупорядочены. выполняются обычные арифме- тические преобразования; результатом является побитовая фун- кция включающего 'или' операндов. Операция применима только к операндам целочисленного типа. 15.11. Логическая операция 'и' Выражение-логического-и: выражение && выражение Операция && группируется слева направо. Она возвращает 1, если оба ее операнда отличны от нуля, и 0 в противном слу- чае. В отличие от & операция && гарантирует вычисление слева направо; более того, если первый операнд равен 0, то значе- ние второго операнда вообще не вычисляется. Операнды не обязаны быть одинакового типа, но каждый из них должен быть либо одного из основных типов, либо указате- лем. результат всегда имеет тип ITN. 15.12. Операция логического 'или' Выражение-логического-или: выражение \!\! выражение Операция \!\! Группируется слева направо. Она возвращает 1, если один из операндов отличен от нуля, и 0 в противном слу- чае. В отличие от операции \! Операция \!\! Гарантирует вы- числение слева направо; более того, если первый операнд от- личен от нуля, то значение второго операнда вообще не вычис- ляется. Операнды не обязаны быть одинакового типа, но каждый из них должен быть либо одного из основных типов, либо указате- лем. Результат всегда имеет тип INT. 15.13. Условная операция Условное-выражение: выражение ? выражение : выражение Условные выражения группируются слево направо. Вычисляется значение первого выражения, и если оно отлично от нуля, то результатом будет значение второго выражения; в противном случае результатом будет значение третьего выражения. Если это возможно, проводятся обычные арифметические преобразова- ния, с тем, чтобы привести второе и третье выражения к обще- му типу; в противном случае, если оба выражения являются указателями одинакового типа, то результат имеет тот же тип; в противном случае одно выражение должно быть указателем, а другое - константой 0, и результат будет иметь тип указате- ля. Вычисляется только одно из второго и третьего выражений. 15.14. Операция присваивания Имеется ряд операций присваивания, каждая из которых группируется слева направо. Все операции требуют в качестве своего левого операнда L-значение, а типом выражения присва- ивания является тип его левого операнда. Значением выражения присваивания является значение, хранимое в левом операнде после того, как присваивание уже будет произведено. Две час- ти составной операции присваивания являются отдельными лек- семами. Выражение-присваивания: L-значение = выражение L-значение += выражение L-значение -= выражение L-значение *= выражение L-значение /= выражение L-значение %= выражение L-значение >>= выражение L-значение <<= выражение L-значение &= выражение L-значение ^= выражение L-значение \!= выражение Когда производится простое присваивание C'=', значение выражения заменяет значение объекта, на которое ссылается L-значение. Если оба операнда имеют арифметический тип, то перед присваиванием правый операнд преобразуется к типу ле- вого операнда. О свойствах выражения вида E1 оп = E2, где Oп - одна из перечисленных выше операций, можно сделать вывод, если учесть, что оно эквивалентно выражению E1 = E1 оп (E2); од- нако выражение E1 вычисляется только один раз. В случае опе- раций += и -= левый операнд может быть указателем, причем при этом (целочисленный) правый операнд преобразуется таким образом, как объяснено в п. 15.4; все правые операнды и все отличные от указателей левые операнды должны иметь арифмети- ческий тип. Используемые в настоящее время компиляторы допускают присваивание указателя целому, целого указателю и указателя указателю другого типа. такое присваивание является чистым копированием без каких-либо преобразований. Такое употребле- ние операций присваивания является непереносимым и может приводить к указателям, которые при использовании вызывают ошибки адресации. Тем не менее гарантируется, что присваива- ние указателю константы 0 дает нулевой указатель, который можно отличать от указателя на любой объект. 15.15. Операция запятая Выражение-с-запятой: выражение , выражение Пара выражений, разделенных запятой, вычисляется слева нап- раво и значение левого выражения отбрасывается. Типом и зна- чением результата является тип и значение правого операнда. Эта операция группируется слева направо. В контексте, где запятая имеет специальное значение, как, например, в списке фактических аргументов функций (п. 15.1) Или в списках ини- циализаторов (п. 16.6), Операция запятая, описываемая в этом разделе, может появляться только в круглых скобках; напри- мер, функция F(A,(T=3,T+2),C) имеет три аргумента, второй из которых имеет значение 5. 16. Описания Описания используются для указания интерпретации, кото- рую язык "C" будет давать каждому идентификатору; они не обязательно резервируют память, соответствующую идентифика- тору. Описания имеют форму Описание: спецификаторы-описания список-описателей необ; Описатели в списке описателей содержат описываемые идентифи- каторы. Спецификаторы описания представляют собой последова- тельность спецификаторов типа и спецификаторов класса памя- ти. Спецификаторы-описания: спецификатор-типа спецификаторы-описания необ спецификатор-класса-памяти спецификатор-описания необ список должен быть самосогласованным в смысле, описываемом ниже. 16.1. Спецификаторы класса памяти Ниже перечисляются спецификаторы класса памяти: Спецификатор-класса-памяти: AUTO STATIC EXTERN REGISTER TYPEDEF Спецификатор TYPEDEF не реализует памяти и называется "спецификатором класса памяти" только по синтаксическим со- ображениям; это обсуждается в п. 16.8. Смысл различных клас- сов памяти был обсужден в п. 12. Описания AUTO, STATIC и REGISTER служат также в качестве определений в том смысле, что они вызывают резервирование нужного количества памяти. В случае EXTERN должно присутст- вовать внешнее определение (п. 18) Указываемых идентификато- ров где-то вне функции, в которой они описаны. Описание REGISTER лучше всего представлять себе как опи- сание AUTO вместе с намеком компилятору, что описанные таким образом переменные будут часто использоваться. Эффективны только несколько первых таких описаний. Кроме того, в регис- трах могут храниться только переменные определенных типов; на PDP-11 это INT, CHAR или указатель. Существует и другое ограничение на использование регистровых переменных: к ним нельзя применять операцию взятия адреса &. При разумном ис- пользовании регистровых описаний можно ожидать получения меньших по размеру и более быстрых программ, но улучшение в будущем генерирования кодов может сделать их ненужными. Описание может содержать не более одного спецификатора класса памяти. Если описание не содержит спецификатора клас- са памяти, то считается, что он имеет значение AUTO, если описание находится внутри некоторой функции, и EXTERN в про- тивном случае. исключение: функции никогда не бывает автома- тическими. 16.2. Спецификаторы типа Ниже перечисляются спецификаторы типа. Спецификатор-типа: CHAR SHORT INT LONG UNSIGNED FLOAT DOUBLE спецификатор-структуры-или-объединения определяющее-тип-имя Слова LONG, SHORT и USIGNED можно рассматривать как при- лагательные; допустимы следующие комбинации: SHORT INT LONG INT USIGNED INT LONG FLOAT Последняя комбинация означает то же, что и DOUBLE. В осталь- ном описание может содержать не более одного спецификатора типа. Если описание не содержит спецификатора типа, то счи- тается, что он имеет значение INT. Спецификаторы структур и объединений обсуждаются в п. 16.5; Описания с определяющими тип именами TYPEDEF обсужда- ются в п. 16.8. 16.3. Описатели Входящий в описание список описателей представляет собой последовательность разделенных запятыми описателей, каждый из которых может иметь инициализатор. Список-описателей: инициализируемый-описатель инициализируемый-описатель, список-описателей инициализируемый-описатель: описатель-инициализатор необ Инициализаторы описываются в п. 16.6. Спецификаторы и описа- ния указывают тип и класс памяти объектов, на которые ссыла- ются описатели. Описатели имеют следующий синтаксис: описатель: идентификатор ( описатель ) * описатель описатель () описатель [константное-выражение необ] Группирование такое же как и в выражениях. 16.4. Смысл описателей Каждый описатель рассматривается как утверждение того, что когда конструкция той же самой формы, что и описатель, появляется в выражении, то она выдает объект указанного типа и указанного класса памяти. Каждый описатель содержит ровно один идентификатор; это именно тот идентификатор, который и описывается. Если в качестве описателя появляется просто идентифика- тор, то он имеет тип, указываемый в специфицирующем заголов- ке описания. Описатель в круглых скобках идентичен описателю без круглых скобок, но круглые скобки могут изменять связи в составных описателях. Примеры смотри ниже. Представим себе описание T DI где T - спецификатор типа (подобный INT и т.д.), а DI - опи- сатель. Предположим, что это описание приводит к тому, что соответствующий идентификатор имеет тип "...T", где "..." пусто, если DI просто отдельный идентификатор (так что тип X в "INT X" просто INT). Тогда , если DI имеет форму *D то содержащийся идентификатор будет иметь тип "... Указатель на T". Если DI имеет форму D() то содержащийся идентификатор имеет тип "... Функция, возв- ращающая T". Если DI имеет форму D[константное-выражение] или D[ ] то содержащийся идентификатор имеет тип "...массив T". В первом случае константным выражением является выражение, значение которого можно определить во время компиляции и ко- торое имеет тип INT. (Точное определение константного выра- жения дано в п. 23). Когда несколько спецификаций вида "мас- сив из" оказываются примыкающими, то создается многомерный массив; константное выражение, задающее границы массивов, может отсутствовать только у первого члена этой последова- тельности. Такое опускание полезно, когда массив является внешним и его фактическое определение, которое выделяет па- мять, приводится в другом месте. Первое константное выраже- ние может быть опущено также тогда, когда за описателем сле- дует инициализация. В этом случае размер определяется по числу приведенных инициализируемых элементов. Массив может быть образован из элементов одного из ос- новных типов, из указателей, из структур или объединений или из других массивов (чтобы образовать многомерный массив). Не все возможности, которые разрешены с точки зрения указанного выше синтаксиса, фактически допустимы. Имеются следующие ограничения: функции не могут возвращать массивы, структуры, объединения или функции, хотя они могут возвра- щать указатели на такие вещи; не существует массивов функ- ций, хотя могут быть массивы указателей на функции. Анало- гично, структуры или объединения не могут содержать функцию, но они могут содержать указатель на функцию. В качестве примера рассмотрим описание INT I, *IP, F(), *FIP(), (*PFI)(); в котором описывается целое I, указатель IP на целое, функ- ция F, возвращающая целое, функция FIP, возвращающая указа- тель на целое, и указатель PFI на функцию, которая возвраща- ет целое. Особенно полезно сравнить два последних описателя. Связь в *FIP() можно представить в виде *(FIP()), так что описанием предполагается, а такой же конструкцией в выраже- нии требуется обращение к функции FIP и последующее исполь- зование косвенной адресации для выдачи с помощью полученного результата (указателя) целого. В описателе (*PFI)() дополни- тельные скобки необходимы, поскольку они точно так же, как и в выражении, указывают, что косвенная адресация через указа- тель на функцию выдает функцию, которая затем вызывается; эта вызванная функция возвращает целое. В качестве другого примера приведем описание FLOAT FA[17], *AFP[17]; в котором описывается массив чисел типа FLOAT и массив ука- зателей на числа типа FLOAT. Наконец, STATIC INT X3D[3][5][7]; описывает статический трехмерный массив целых размером 3*5*7. более подробно, X3D является массивом из трех элемен- тов; каждый элемент является массивом пяти массивов; каждый последний массив является массиво