Язык программирования Си. Производственно-внедренческий кооператив "И Н Т Е Р Ф Е Й С" Диалоговая Единая Мобильная Операционная Система Демос/P 2.1 Язык программирования Си. Москва 1988 Описан универсальный язык программирования Си. Приве- дены структура и синтаксис языка, правила написания прог- рамм, даны начальные сведения о взаимодействии программ на Си с операционной системой Демос.  * 1. ВВЕДЕНИЕ Язык Си - это универсальный язык программирования, для которого характерны экономичность выражения, современный набор операторов и типов данных. Язык Си не является ни языком "очень высокого уровня", ни "большим" языком, и не предназначается для некоторой специальной области примене- ния, но отсутствие ограничений и общность языка делают его для многих задач более удобным и эффективным, чем языки, предположительно более мощные. Операционная система, компи- лятор с языка Си и по существу все прикладные программы сис- темы "ДЕМОС" написаны на Си. Язык Си не связан с какими- либо определенными аппаратными средствами или системами, и на нем легко писать программы, которые можно пропускать без изменений на любой ЭВМ, имеющей Си-компилятор. Язык Си является универсальным языком программирования. Он первоначально появился в операционной системе UNIX, и развивался как основной язык систем, совместимых с ОС UNIX. Сам язык , однако, не связан с какой-либо одной операционной системой или машиной; и хотя его называют языком системного программирования, так как он удобен для написания операцион- ных систем, он может использоваться для написания любых больших вычислительных программ, программ для обработки текстов и баз данных. Язык Си - это язык относительно "низкого уровня". Это означает, что Си имеет дело с объектами того же вида, что и большинство ЭВМ, а именно, с символами, числами и адресами. Они могут объединяться и пересылаться посредством обычных арифметических и логических операций, осуществляемых реаль- ными ЭВМ. В языке Си отсутствуют операции, имеющие дело непос- редственно с составными объектами, такими как строки симво- лов, множества, списки или с массивами, рассматриваемыми как целое. Здесь, например, нет никакого аналога операциям PL/1, оперирующим с массивами и строками. Язык не предоставляет никаких других возможностей распределения памяти, кроме ста- тического определения и механизма стеков, обеспечиваемого локальными переменных функций. Сам по себе язык Си не обес- печивает никаких возможностей ввода-вывода. Все эти меха- низмы высокого уровня должны обеспечиваться явно вызываемыми функциями. Аналогично, язык Си предлагает только простые, последо- вательные конструкции управления: проверки, циклы, группиро- вание и подпрограммы, но не мультипрограммирование, парал- лельные операции, синхронизацию или сопрограммы. Удержание языка в скромных размерах дает реальные преи- мущества. Так как Си относительно мал, он не требует много места для своего описания и может быть быстро выучен. -1- Компилятор с Си может быть простым и компактным. Это обес- печивает высокую степень мобильности языка. Поскольку типы данных и структуры управления, имеющиеся в Си, непосредст- венно поддерживаются большинством существующих ЭВМ, библио- тека, необходимая во время прогона изолированных программ, оказывается очень маленькой. На СМ-4, например , она содер- жит только программы для 32-битового умножения и деления и для упрятывания и восстановления регистров при входе в функ- цию. Конечно, каждая реализация обеспечивает исчерпывающую, совместимую библиотеку функций для выполнения операций ввода-вывода, обработки строк и распределения памяти, но так как обращение к ним осуществляется только явно, можно, если необходимо, избежать их вызова; эти функции могут быть ком- пактно написаны на самом Си. Опять же из-за того, что язык Си отражает возможности современных компьютеров, программы на Си оказываются доста- точно эффективными, так что не возникает побуждения писать вместо этого программы на языке ассемблера. Хотя Си соот- ветствует возможностям многих ЭВМ, он не зависит от какой- либо конкретной архитектуры машины и в силу этого без особых усилий позволяет писать "переносимые" программы, т.е. прог- раммы, которые можно пропускать без изменений на различных аппаратных средствах. Язык Си не является языком со строгими типами данных в смысле Паскаля или Алгола-68. Он сравнительно снисходителен к преобразованию данных, хотя и не будет буйно преобразовы- вать типы данных подобно языку PL/1. Компилятор не предус- матривает никакой проверки индексов массивов, типов аргумен- тов и т.д. во время выполнения программы. В тех ситуациях, когда желательна строгая проверка типов, используется специальная программа lint. Программа lint не генерирует машинного кода, а делает очень строгую проверку всех тех сторон программы, которые можно проконтро- лировать во время компиляции и загрузки. Она определяет несоответствие типов, несовместимость аргументов, неисполь- зованные или очевидным образом неинициализированные перемен- ные, потенциальные трудности переносимости и т.д. Из за того, что в языке отсутствуют средства ввода/вывода и т.п., при программировании на нем существен- ную роль играет библиотека стандартных программ, осуществля- ющих взаимодействие с системой. Во всех системах, совмести- мых с ОС UNIX, к которым относится и ДЕМОС, существует сов- местимый набор программ для ввода/вывода, управления памятью, преобразования данных и выполняющих другие функции, использование которых обеспечивает возможность переноса программ на другие ЭВМ. В данном документе описывается язык Си, расширения, обеспечиваемые специальным препроцессором (фактически они -2- вошли уже в понятие "язык Си"), стандартная библиотека ввода/вывода, и даются начальные сведения о взаимодействии программ на Си с ОС ДЕМОС. Полное описание библиотечных программ имеется в руководстве программиста ОС ДЕМОС (части 3 и 4), и в оперативной документации man(2) и man(3). Хоро- шим учебником по языку Си является книга [1], краткое фор- мальное описание языка приведено в [2]. В тексте встречаются примечания, относящиеся к реализа- ции языка Си в ОС ДЕМОС. Такие примечания выделяются верти- кальной чертой справа (как выделен данный абзац).  * 2. СИНТАКСИЧЕСКАЯ НОТАЦИЯ В используемой в этом руководстве синтаксической нота- ции синтаксические категории записываются русскими буквами и символом "_", а все остальные символы рассматриваются как литерные (то есть изображающие сами себя). Альтернативные категории перечисляются на отдельных строчках. Необязатель- ный символ, терминальный или нетерминальный, указывается индексом "необ", так что { выражение } необ указывает на необязательное выражение, заключенное в фигур- ных скобках. Синтаксис описывается в Приложении 1. Если описание не помещается на одной строке, оно про- должается на следующей с некоторым сдвигом вправо, например: описание_структуры: спецификатор_типа список_описателей_структуры Здесь следует читать: описание_структуры: спецификатор_типа список_описателей_структуры Если сделан разбор входного потока на лексемы вплоть до данного символа, то в качестве следующей лексемы берется самая длинная строка символов, IBM/370 (OS-360) 7 символов, 1 регистр VAX 11 (UNIX) 7 символов, 2 регистра 2.1. Ключевые слова Следующие идентификаторы зарезервированы для использо- вания в качестве ключевых слов и не могут использоваться иным образом: -3- int extern else char register for float typedef do double static while struct goto switch union return case long sizeof default short break entry unsigned continue auto if Ключевое слово entry в настоящее время не используется каким-либо компилятором; оно зарезервировано для использова- ния в будущем. В некоторых реализациях резервируются также слова fortran и asm. 2.2. Константы Имеется несколько видов констант, которые перечислены ниже. 2.2.1. Целые константы Целая константа, состоящая из последовательности цифр, считается восьмеричной, если она начинается с 0 (цифра нуль), и десятичной в противном случае. Цифры 8 и 9 имеют восьмеричные значения 10 и 11 соответственно. Последова- тельность цифр, которой предшествуют символы (нуль, х- маленькое) или (нуль X-большое), рассматривается как шестнадцатиричное целое. Шестнадцатиричные цифры включают буквы от a (маленькое) или A (большое) до f (маленькое) или F (большое) со значениями от 10 до 15. Десятичная константа, величина которой превышает наибольшее машинное целое со зна- ком, считается длинной; восьмеричная или шестнадцатиричная константа, которая превышает наибольшее машинное целое без знака, также считается длинной. 2.2.2. Длинные (long) константы Десятичная, восьмеричная или шестнадцатиричная конс- танта, за которой непосредственно следует l (эль-маленькое) или L (эль-большое), является длинной константой. На некото- рых машинах целые и длинные значения могут рассматриваться как идентичные. 2.2.3. Символьные константы Символьная константа - это символ, заключенный в оди- ночные кавычки, как, например, 'х'. Значением символьной константы является численное значение этого символа в машин- ном представлении набора символов. -4- Некоторые неграфические символы, одиночная кавычка ' и обратная косая черта \ могут быть представлены двумя симво- лами в соответствии со следующей таблицей условных последо- вательностей: Название Код Обозначение новая строка 012 \n горизонтальная табуляция 011 символ возврата на одну 010 позицию возврат каретки 015 \r переход на новую страницу 014 \f обратная косая черта 0133 \\ одиночная кавычка 047 \' произвольный символ 0ddd \ddd Условная последовательность \ddd состоит из обратной косой черты, за которой следуют 1, 2 или 3 восьмеричных цифры, которые рассматриваются как задающие значение желае- мого символа. Специальным случаем этой конструкции является последовательность \0 (за нулем не следует цифра), которая определяет нулевой символ. Если следующий за обратной косой чертой символ не совпадает с одним из указанных, то обратная косая черта игнорируется. 2.2.4. Вещественные константы Вещественная константа состоит из целой части, десятич- ной точки, дробной части, буквы e (маленькая) или E (боль- шая) и целой экспоненты с необязательным знаком. Как целая, так и дробная часть являются последовательностью цифр. Либо целая, либо дробная часть (но не обе) может отсутствовать; либо десятичная точка, либо e и экспонента (но не то и дру- гое одновременно) может отсутствовать. Вещественные конс- танты в большинстве реализаций считаются константами двойной точности. 2.3. Строки Строка - это последовательность символов, заключенная в двойные кавычки, как, например, "...". Строка имеет тип мас- сив символов и класс памяти static (см. ниже). Строка иници- ализирована указанными в ней символами. Все строки, даже идентично записанные, считаются различными. Компилятор помещает в конец каждой строки нулевой байт \0, с тем чтобы просматривающая строку программа могла определить ее конец. Перед стоящим внутри строки символом двойной кавычки " дол- жен быть поставлен символ обратной косой черты \; кроме того, могут использоваться те же условные последователь- ности, что и в символьных константах. Обратная косая черта \, за которой непосредственно следует символ новой строки, -5- игнорируется. Имеются макропроцессорные средства, позволяющие объеди- нять совпадающие строки при трансляции с целью экономии памяти (см. команду xstr). 2.4. Характеристики аппаратных средств Следующая ниже таблица суммирует некоторые свойства аппаратного оборудования, которые меняются от машины к машине. Хотя они и влияют на переносимость программ, на практике они представляют меньшую проблему, чем это может казаться заранее. Таблица 1. ----------------------------------------- | CM-ЭВМ IBM 370 (OS) VAX-11 | | КОИ-8 ebcdic ASCII | | char 8 бит 8 бит 8 бит | | int 16 32 32 | | short 16 16 16 | | long 32 32 32 | | float 32 32 32 | | double 64 64 64 | | range -38/+38 -76/+76 -76/+76 | |_______________________________________|  * 3. ОБ'ЕКТЫ ЯЗЫКА СИ 3.1. Интерпретация идентификаторов С каждым идентификатором в Си связано два атрибута: его класс памяти и его тип. Класс памяти определяет место и время хранения памяти, связанной с идентификатором; тип определяет смысл величин, находящихся в памяти, определенной под идентификатором. Имеются четыре класса памяти: автоматическая, статичес- кая, внешняя и регистровая. Автоматические переменные явля- ются локальными для каждого вызова блока и исчезают при выходе из этого блока. Статические переменные являются локальными, но сохраняют свои значения даже после того, как управление передается за пределы блока. Внешние переменные существуют и сохраняют свои значения в течение выполнения всей программы и могут использоваться для связи между функ- циями, в том числе и между независимо скомпилированными функциями. Регистровые переменные хранятся (если это воз- можно) в быстрых регистрах машины; подобно автоматическим переменным они являются локальными для каждого блока и исче- зают при выходе из этого блока. В языке Си предусмотрено несколько основных типов объектов: -6- Символьный. - Объекты, описанные как символы (char), достаточно велики, чтобы хранить любой член из соответствующего данной реализации внутреннего набора символов, и если действительный символ из этого набора символов хра- нится в символьной переменной, то ее значение эквива- лентно целому коду этого символа. В символьных пере- менных можно хранить и другие величины, но реализация будет машинно-зависимой. (На СМ ЭВМ значение символь- ных переменных изменяется от -0177 до 0177.) Целый. - Можно использовать до трех размеров целых, описывае- мых как short int, int и long int. Длинные целые занимают не меньше памяти, чем короткие, но в конк- ретной реализации может оказаться, что либо короткие целые, либо длинные целые, либо те и другие будут эквивалентны простым целым. "Простые" целые имеют естественный размер, предусматриваемый архитектурой используемой машины; другие размеры вводятся для удовлетворения специальных потребностей. Беззнаковый. - Целые без знака, описываемые как unsigned, подчиня- ются законам арифметики по модулю 2**n, где n - число битов в их представлении. (На CM-ЭВМ длинные вели- чины без знака не предусмотрены). Вещественный. - Вещественные одинарной точности (float) и веществен- ные двойной точности (double) в некоторых реализациях могут быть синонимами. (На СМ ЭВМ float занимает 32 бита памяти, а double - 64). В языке нет логического типа данных, а в качестве логических значений используются целые "0" - "ложь" и "1" - "истина" (при проверках любое целое, не равное 0, трактуется как "истина"). Поскольку объекты упомянутых выше типов могут быть разумно интерпретированы как числа, эти типы будут назы- ваться арифметическими. Типы char и int всех размеров сов- местно будут называться целочисленными. Типы float и double совместно будут называться вещественными типами. Кроме основных арифметических типов существует концеп- туально бесконечный класс производных типов, которые образу- ются из основных типов следующим образом: - массивы объектов большинства типов; - функции, которые возвращают объекты заданного типа; -7- - указатели на объекты данного типа; - структуры, содержащие последовательность объектов различных типов; - объединения, способные содержать один из нескольких объектов различных типов. Вообще говоря, эти методы построения объектов могут применяться рекурсивно. 3.2. Объекты и l_значения Объект является доступным обработке участком памяти; l_значение (левое значение) - это выражение, ссылающееся на объект. Очевидным примером выражения l_значения является идентификатор. Существуют операции, результатом которых являются l_значения; если, например, e - выражение типа ука- затель, то *e является выражением l_значения, ссылающимся на тот объект, на который указывает е. Название "l_значение" происходит от выражения присваивания e1=e2, в котором левая часть должна быть выражением l_значения. При последующем обсуждении каждой операции будет указываться, ожидает ли она операндов l_значения и выдает ли она l_значение. 3.3. Преобразования Ряд операций может в зависимости от своих операндов вызывать преобразование значения операнда из одного типа в другой. В этом разделе объясняются результаты, которые сле- дует ожидать от таких преобразований. В конце подводятся итоги преобразований, требуемые большинством обычных опера- ций; эти сведения дополняются необходимым образом при обсуж- дении каждой операции. 3.3.1. Символы и целые Символ или короткое целое можно использовать всюду, где можно использовать целое. Во всех случаях значение преобра- зуется к целому. Преобразование более короткого целого к более длинному всегда сопровождается знаковым расширением; целые являются величинами со знаком. Осуществляется или нет знаковое расширение для символов, зависит от используемой машины, на СМ-ЭВМ такое преобразование осуществляется так, что русские буквы при прямом преобразовании получат отрица- тельные коды. Область значений символьных переменных на CM-ЭВМ меняется от -128 до 127; символы из набора ASCII имеют положительные значения. Символьная константа, задан- ная с помощью восьмеричной условной последовательности, под- вергается знаковому расширению и может оказаться отрицатель- ной; например, '\377' имеет значение -1. -8- Когда более длинное целое преобразуется в более корот- кое или в char, оно обрезается слева; лишние биты просто отбрасываются. 3.3.2. Типы float и double Вся вещественная арифметика в Си выполняется с двойной точностью. Каждый раз, когда объект типа float появляется в выражении, он удлиняется до double посредством добавления нулей в его дробную часть. Когда объект типа double должен быть преобразован к типу float, например, при присваивании, перед усечением double округляется до длины float. Единственное исключение может быть сделано в компилято- рах для ЭВМ, на которых нет аппаратных операций над числами типа double (например, СМ-4). Уточнить это можно по описа- нию компилятора (команда cc). 3.3.3. Вещественные и целочисленные величины Преобразование вещественных значений к целочисленному типу в некоторой степени машинно-зависимо; в частности, нап- равление усечения отрицательных чисел меняется от машине к машине. Результат не определен, если значение не помещается в предоставляемое пространство. Преобразование целочисленных значений в вещественные выполняется без осложнений. Может произойти некоторая потеря точности, если для результата не хватит длины мантиссы. 3.3.4. Указатели и целые Целое или длинное целое может быть прибавлено к указа- телю или вычтено из него; в этом случае первая величина пре- образуется так, как указывается в описании операции сложе- ния. Два указателя на объекты одинакового типа могут быть вычтены; в этом случае результат преобразуется к целому, как указывается в описании операции вычитания. 3.3.5. Целое без знака Всякий раз, когда целое без знака объединяется с прос- тым целым, простое целое преобразуется в целое без знака и результат оказывается целым без знака. Значением является наименьшее целое без знака, соответствующее целому со знаком (по модулю 2**размер слова). В двоичном дополнительном представлении это преобразование является чисто умозритель- ным и не изменяет фактическую комбинацию битов. Когда целое без знака преобразуется к типу long, значе- ние результата совпадает со значением целого без знака. -9- Таким образом, это преобразование сводится к добавлению нулей слева. 3.3.6. Арифметические преобразования Подавляющее большинство операций вызывает преобразова- ние и определяет типы результата аналогичным образом. Приво- димая ниже схема в дальнейшем будет называться "обычными арифметическими преобразованиями". Сначала любые операнды типа char или short преобразуются в int, а любые операнды типа float преобразуются в double. Затем, если какой-либо операнд имеет тип double, то другой преобразуется к типу double, и это будет типом результата. В противном случае, если какой-либо операнд имеет тип long, то другой операнд преобразуется к типу long, и это и будет типом результата. В противном случае, если какой-либо операнд имеет тип unsigned, то другой операнд преобразуется к типу unsigned, и это будет типом результата. В противном случае оба операнда будут иметь тип int, и это будет типом результата.  * 4. ВЫРАЖЕНИЯ Старшинство операций в выражениях совпадает с порядком следования основных подразделов настоящего раздела, начиная с самого высокого уровня старшинства. Так, например, выраже- ниями, указываемыми в качестве операндов операции + (п.0.4), являются выражения, определенные в п.п.0.1-0.3. Внутри каж- дого подраздела операции имеют одинаковое старшинство. В каждом подразделе для описываемых там операций указывается их ассоциативность слева или справа. Старшинство и ассоциа- тивность всех операций в выражениях резюмируются в граммати- ческой сводке в приложении. В противном случае порядок вычислений выражений не определен. В частности, компилятор может вычислять подвыра- жения в том порядке, который он находит наиболее эффектив- ным, даже если эти подвыражения приводят к побочным эффек- там. Порядок, в котором происходят побочные эффекты, не спе- цифицируется. Выражения, включающие коммутативные и ассоциа- тивные операции (*,+,&,|,^), могут быть переупорядочены про- извольным образом даже при наличии круглых скобок; в этом случае необходимо использовать явные промежуточные перемен- ные. При вычислении выражений обработка переполнения и про- верка при делении являются машинно-зависимыми. Большинство реализаций языка Си (в том числе и в ОС ДЕМОС) игнорируют переполнение целых; обработка ошибки при делении на 0 и при всех особых случаях в операциях с вещественными числами меняется от машины к машине и обычно выполняется с помощью библиотечной функции. -10- 4.1. Первичные выражения Первичные выражения, включающие ., ->, индексацию и обращения к функциям, группируются слева направо. первичное выражение: идентификатор константа строка (выражение) первичное_выражение [выражение] первичное_выражение (список_выражений) необ первичное_l_значение . Идентификатор первичное_выражение -> идентификатор список_выражений: выражение список_выражений, выражение Идентификатор является первичным выражением при условии, что он описан подходящим образом, как это обсуждается ниже. Тип идентификатора определяется его описанием. Если, однако, типом идентификатора является массив ..., то значением выра- жения, состоящего из этого идентификатора, является указа- тель на первый объект в этом массиве, а типом выражения будет указатель на .... Более того, идентификатор массива не является выражением l_значения. Подобным образом интерпрети- руется идентификатор, который описан как функция, возвращаю- щая .... За исключением того случая, когда он используется в позиции имени функции при обращении, преобразуется в указа- тель на функцию, которая возвращает .... Константа является первичным выражением. В зависимости от ее формы типом константы может быть int, long или double. Строка является первичным выражением. Исходным ее типом является массив символов; но следуя тем же самым правилам, которые приведены выше для идентификаторов, он модифициру- ется в указатель на символы, и результатом является указа- тель на первый символ строки. (Имеется исключение в некото- рых инициализаторах; см. ниже.) Выражение в круглых скобках является первичным выраже- нием, тип и значение которого идентичны типу и значению этого выражения без скобок. Наличие круглых скобок не вли- яет на то, является ли выражение l_значением или нет. Первичное выражение, за которым следует выражение в квадратных скобках, является первичным выражением. Это выра- жение с индексом. Обычно первичное выражение имеет тип ука- затель на ..., индексное выражение имеет тип int, а типом результата является "...". Выражение e1[e2] по определению идентично выражению * ((e1) + (e2)). Все, что необходимо -11- для понимания этой записи, содержится в этом разделе; воп- росы, связанные с понятием идентификаторов и операций * и + рассматриваются в п.п. 0.1, 0.2 и 0.4 соответственно; выводы суммируются ниже. Обращение к функции является первичным выражением, за которым следует заключенный в круглые скобки возможно пустой список выражений, разделенных запятыми, которые и представ- ляют собой фактические аргументы функции. Первичное выраже- ние должно быть типа функция, возвращающая ..., а результат обращения к функции имеет тип "...". Как указывается ниже, ранее не встречавщийся идентификатор, за которым непосредст- венно следует левая круглая скобка, считается описанным по контексту, как представляющий функцию, возвращающую целое; следовательно чаще всего встречающийся случай функции, возв- ращающей целое значение, не нуждается в описании. Перед обращением любые фактические аргументы типа float преобразуются к типу double, любые аргументы типа char или short преобразуются к типу int, и, как обычно, имена масси- вов преобразуются в указатели. Никакие другие преобразования не выполняются автоматически; в частности, не сравниваются типы фактических аргументов с типами формальных аргументов. Если преобразование необходимо, используйте явное преобразо- вание. При подготовке к вызову функции делается копия каждого фактического параметра; таким образом, все передачи аргумен- тов в языке Си осуществляются строго по значению. Функция может изменять значения своих формальных параметров, но эти изменения не влияют на значения фактических параметров. С другой стороны, имеется возможность передавать указатель, при этом функция может изменять значение объекта, на который этот указатель указывает. Порядок вычисления аргументов в языке не определен; различные компиляторы вычисляют по раз- ному. Допускаются рекурсивные обращения к любой функции. Первичное выражение, за которым следует точка и иденти- фикатор, является выражением. Первое выражение должно быть l_значением, именующим структуру или объединение, а иденти- фикатор должен быть именем члена структуры или объединения. Результатом является l_значение, ссылающееся на поименован- ный член структуры или объединения. Первичное выражение, за которым следует стрелка (сос- тавленная из знаков - и >) и идентификатор, является выраже- нием. Первое выражение должно быть указателем на структуру или объединение, а идентификатор должен именовать член этой структуры или объединения. Результатом является l_значение, ссылающееся на поименованный член структуры или объединения, на который указывает указательное выражение. -12- Следовательно, выражение e1->mos является тем же самым, что и выражение (*e1).mos. Структуры и объединения рассмат- риваются ниже. Приведенные здесь правила использования структур и объединений не навязываются строго, для того чтобы иметь возможность обойти механизм типов (см. "Допол- нительная информация о типах"). 4.2. Унарные операции Выражение с унарными операциями группируется справа налево. унарное_выражение: * выражение & l_значение - выражение ! выражение ~ выражение ++ l_значение -- l_значение l_значение ++ l_значение -- (имя-типа) выражение sizeof выражение sizeof (имя_типа) Унарная операция * означает косвенную адресацию: выражение должно быть указателем, а результатом является l_значение, ссылающееся на тот объект, на который указывает выражение. Если типом выражения является указатель на ..., то типом результата будет "...". Результатом унарной операции & является указатель на объект, к которому ссылается l_значение. Если l_значение имеет тип "...", то типом результата будет указатель на .... Результатом унарной операции - (минус) является ее опе- ранд, взятый с противоположным знаком. Для величины типа unsigned результат получается вычитанием ее значения из 2**n (два в степени n), где n-число битов в int. Унарной операции + (плюс) не существует. Результатом операции логического отрицания ! является 1, если значение ее операнда равно 0, и 0, если значение ее операнда отлично от нуля. Результат имеет тип int. Эта опе- рация применима к любому арифметическому типу или указате- лям. Операция ~ (символ "тильда", находится на клавише ^ в нижнем регистре) дает обратный код (или дополнение до еди- ницы) своего операнда. Выполняются обычные арифметические преобразования. Операнд должен быть целочисленного типа. -13- Объект, на который ссылается операнд l_значения пре- фиксной операции ++, увеличивается. Значением является новое значение операнда, но это не l_значение. Выражение ++х экви- валентно х += 1 . Информацию о преобразованиях смотри в раз- боре операции сложения (п. 0.4) и операции присваивания (п. 0.14). Префиксная операция -- аналогична префиксной операции ++, но приводит к уменьшению своего операнда l_значения. При применении постфиксной операции ++ к l_значению результатом является значение объекта, на который ссылается l_значение. После того, как результат принят к сведению, объект увеличивается точно таким же образом, как и в случае префиксной операции ++. Результат имеет тот же тип, что и выражение l_значения. При применении постфиксной операции -- к l_значению результатом является значение объекта, на который ссылается l_значение. После того, как результат принят к сведению, объект уменьшается точно таким же образом, как и в случае префиксной операции --. Результат имеет тот же тип, что и выражение l_значения. Заключенное в круглые скобки имя типа данных, стоящее перед выражением, вызывает преобразование значения этого выражения к указанному типу. Эта конструкция называется перевод (cast). Имена типов описываются в следующем разделе. Операция sizeof выдает размер своего операнда в байта. (Понятие байт в языке не определено, разве только, как зна- чение операции sizeof. Однако во всех существующих реализа- циях байтом является пространство, необходимое для хранения объекта типа char). При применении к массиву результатом является полное число байтов в массиве. Размер определяется из описаний объектов в выражении. Это выражение семантически является целой константой и может быть использовано в любом месте, где требуется константа. Основное применение эта опе- рация находит при вызове процедур, подобных распределителям памяти, и в системах ввода- вывода. Операция sizeof может быть также применена и к заклю- ченному в круглые скобки имени типа. В этом случае она выдает размер в байтах объекта указанного типа. Конструкция sizeof (тип) рассматривается как целое, так что выражение sizeof (тип) - 2 эквивалентно выражению (sizeof (тип)) - 2. 4.3. Мультипликативные операции Мультипликативные операции *, /, и % группируются слева направо. Выполняются обычные арифметические преобразования. -14- мультипликативное_выражение: выражение * выражение выражение / выражение выражение % выражение Бинарная операция * означает умножение. Операция * ассоциативна, и выражения с несколькими умножениями на одном и том же уровне могут быть перегруппированы компилятором. Бинарная операция / означает деление. При делении поло- жительных целых осуществляется усечение по направлению к нулю, но если один из операндов отрицателен, то форма усече- ния зависит от используемой машины. Остаток имеет тот же знак, что и делимое. Всегда справедливо, что (a/b)*b+a%b равно a (если b не равно 0). Бинарная операция % выдает остаток от деления первого выражения на второе. Выполняются обычные арифметические пре- образования. Операнды должны быть целого типа. 4.4. Аддитивные операции Аддитивные операции + и - группируются слева направо. Выполняются обычные арифметические преобразования. Для каж- дой операции имеются некоторые дополнительные возможности, связанные с типами операндов. аддитивное_выражение: выражение + выражение выражение - выражение Результатом операции + является сумма операндов. Можно также складывать указатель на объект в массиве и значение любого целочисленного типа. Последнее преобразуется в адресное смещение посредством умножения его на длину объекта, на который указывает этот указатель. Результатом является ука- затель того же самого типа, что и исходный указатель, кото- рый указывает на другой объект в том же массиве, смещенный соответствующим образом относительно первоначального объекта. Таким образом, если p является указателем объекта в массиве, то выражение p+1 является указателем на следующий объект в этом массиве. Никакие другие комбинации типов для указателей не раз- решаются. Операция + ассоциативна, и выражение с несколькими сло- жениями на одном и том же уровне могут быть переупорядочены компилятором. -15- Результатом операции - является разность операндов. Выполняются обычные арифметические преобразования. Кроме того, из указателя может быть вычтено значение любого цело- численного типа, причем, проводятся те же самые преобразова- ния, что и при операции сложения. Если вычитаются два указателя на объекты одинакового типа, то результат преобразуется (делением на длину объекта) к типу int, представляя собой число объектов, разделяющих указываемые объекты. Если эти указатели не на объекты из одного и того же массива, то такое преобразование, вообще говоря, даст неожиданные результаты, потому что даже указа- тели на объекты одинакового типа не обязаны отличаться на величину, кратную длине объекта. 4.5. Операции сдвига Операции сдвига << и >> группируются слева направо. Для обеих операций проводятся обычные арифметические преоб- разования их операндов, каждый из которых должен быть целого типа. Затем правый операнд преобразуется к типу int; резуль- тат имеет тип левого операнда. Результат не определен, если правый операнд отрицателен или больше или равен, чем длина объекта в битах. выражение_сдвига: выражение << выражение выражение >> выражение Значением выражения e1<<e2 является e1 (интерпретируемое как комбинация битов), сдвинутое влево на e2 битов; освобождаю- щиеся биты заполняются нулем. Значением выражения e1>>e2 является e1, сдвинутое вправо на e2 битовых позиций. Если e1 имеет тип unsigned, то сдвиг вправо гарантированно будет логическим (заполнение нулем); в противном случае сдвиг может быть (как на CM-ЭВМ) арифметическим (освобождающиеся биты заполняются копией знакового бита). 4.6. Операции отношения Операции отношения группируются слева направо, но этот факт не очень полезен; выражение a<b<c не означает того, что оно ,казалось бы, должно означать, а означает ((a<b)<c). выражение_отношения: выражение < выражение выражение > выражение выражение <= выражение выражение >= выражение Операции < (меньше), > (больше), <= (меньше или равно) и >= (больше или равно) дают 0, если указанное отношение ложно, и 1, если оно истинно. Результат имеет тип int. Выполняются -16- обычные арифметические преобразования. Могут сравниваться два указателя; результат зависит от относительного располо- жения указываемых объектов в адресном пространстве. Сравне- ние указателей переносимо только в том случае, если указа- тели указывают на объекты из одного и того же массива. 4.7. Операции равенства выражение_равенства: выражение == выражение выражение != выражение Операции == (равно)