Язык программирования Си Брайан Керниган, Деннис Ритчи 3е издание Версия 1 Table of Contents
Скачать 2.33 Mb.
|
А 7.7. Аддитивные операторы Аддитивные операторы + и - выполняются слева направо. Если операнды имеют арифметический тип, то осуществляются обычные арифметические преобразования. Для каждого оператора существует еще несколько дополнительных сочетаний типов. аддитивное-выражение: мультипликативное-выражение аддитивное-выражение + мультипликативное-выражение аддитивное-выражение - мультипликативное-выражение Результат выполнения оператора + есть сумма его операндов. Указатель на объект в массиве можно складывать с целочисленным значением. При этом последнее преобразуется в адресное смещение посредством умножения его на размер объекта, на который ссылается указатель. Сумма является указателем на объект того же типа; только ссылается этот указатель на другой объект того же массива, отстоящий от первоначального соответственно вычисленному смещению. Так, если Р — указатель на объект в массиве, то Р+1 — указатель на следующий объект того же массива. Если полученный в результате суммирования указатель указывает за границы массива, то, кроме случая, когда он указывает на место, находящееся непосредственно за концом массива, результат будет неопределенным. Возможность для указателя указывать на элемент, расположенный сразу за концом массива, является новой. Тем самым узаконена общепринятая практика организации циклического перебора элементов массива. Результат выполнения оператора - (минус) есть разность операндов. Из указателя можно вычитать значение любого целочисленного типа с теми же преобразованиями и при тех же условиях, что и в сложении. Если к двум указателям на объекты одного и того же типа применить оператор вычитания, то в результате получится целочисленное значение со знаком, представляющее собой расстояние между объектами, на которые указывают эти указатели; указатель на следующий объект на 1 больше указателя на предыдущий объект. Тип результата зависит от реализации; в стандартном заголовочном файле он определен под именем ptrdiff_t . Значение не определено, если указатели указывают на объекты не одного и того же массива; однако если Р указывает на последний элемент массива, то Р+1-Р имеет значение, равное 1. А 7.8. Операторы сдвига Операторы сдвига << и >> выполняются слева направо. Для обоих операторов каждый операнд должен иметь целочисленный тип, и каждый из них подвергается целочисленному повышению. Тип результата совпадает с повышенным типом левого операнда. Результат не определен, если правый операнд отрицателен или его значение превышает число битов в типе левого выражения или равно ему. сдвиговое-выражение: аддитивное-выражение сдвиговое-выражение >> аддитивное-выражение сдвиговое-выражение << аддитивное-выражение Значение Е1<<Е2 равно значению Е1 (рассматриваемому как цепочка битов), сдвинутому влево на Е2 битов; при отсутствии переполнения такая операция эквивалентна умножению на 2 Е2 . Значение Е1>>Е2 равно значению Е1 , сдвинутому вправо на Е2 битовые позиции. Если Е1 — беззнаковое или имеет неотрицательное значение, то правый сдвиг эквивалентен делению на 2 Е2 , в противном случае результат зависит от реализации. А 7.9. Операторы отношения Операторы отношения выполняются слева направо, однако это свойство едва ли может оказаться полезным; согласно грамматике языка выражение a(а, а результат вычисления авыражение-отношения: сдвиговое-выражение выражение-отношения < сдвиговое-выражение выражение-отношения > сдвиговое-выражение выражение-отношения <= сдвиговое-выражение выражение-отношения >= сдвиговое-выражение Операторы: < (меньше), > (больше), <= (меньше или равно) и >= (больше или равно) — все выдают 0, если специфицируемое отношение ложно, и 1, если оно истинно. Тип результата — int . Над арифметическими операндами выполняются обычные арифметические преобразования. Можно сравнивать указатели на объекты одного и того же типа (без учета квалификаторов); результат будет зависеть от относительного расположения в памяти. Допускается, однако, сравнение указателей на разные части одного и того же объекта: если два указателя указывают на один и тот же простой объект, то они равны; если они указывают на элементы одной структуры, то указатель на элемент с более поздним объявлением в структуре больше; если указатели указывают на элементы одного и того же объединения, то они равны; если указатели указывают на элементы некоторого массива, то сравнение этих указателей эквивалентно сравнению их индексов. Если Р указывает на последний элемент массива, то Р+1 больше, чем Р , хотя Р+1 указывает за границы массива. В остальных случаях результат сравнения не определен. Эти правила несколько ослабили ограничения, установленные в первой редакции языка. Они позволяют сравнивать указатели на различные элементы структуры и объединения и легализуют сравнение с указателем на место, которое расположено непосредственно за концом массива. А 7.10. Операторы равенства выражение-равенства: выражение-отношения выражение-равенства == выражение-отношения выражение-равенства != выражение-отношения Операторы == (равно) и != (не равно) аналогичны операторам отношения с той лишь разницей, что имеют более низкий приоритет. (Таким образом, aОператоры равенства подчиняются тем же правилам, что и операторы отношения. Кроме того, они дают возможность сравнивать указатель с целочисленным константным выражением, значение которого равно нулю, и с указателем на void (см. А6.6.). А 7.11. Оператор побитового И И-выражение: выражение-равенства И-выражение & выражение-равенства Выполняются обычные арифметические преобразования; результат — побитовое И операндов. Оператор применяется только к целочисленным операндам. А 7.12. Оператор побитового исключающего ИЛИ исключающее-ИЛИ-выражение: И-выражение исключающее-ИЛИ-выражение ^ И-выражение Выполняются обычные арифметические преобразования; результат — побитовое исключающее ИЛИ операндов. Оператор применяется только к целочисленным операндам. А 7.13. Оператор побитового ИЛИ ИЛИ-выражение: исключающее-ИЛИ-выражение ИЛИ-выражение | исключающее-ИЛИ-выражение Выполняются обычные арифметические преобразования; результат — побитовое ИЛИ операндов. Оператор применяется только к целочисленным операндам. А 7.14. Оператор логического И логическое-И-выражение: ИЛИ-выражение логическое-И-выражение && ИЛИ-выражение Операторы && выполняются слева направо. Оператор && выдает 1, если оба операнда не равны нулю, и 0 в противном случае. В отличие от & , && гарантирует, что вычисления будут проводиться слева направо: вычисляется первый операнд со всеми побочными эффектами; если он равен 0, то значение выражения есть 0. В противном случае вычисляется правый операнд, и, если он равен 0, то значение выражения есть 0, в противном случае оно равно 1. Операнды могут принадлежать к разным типам, но при этом каждый из них должен иметь либо арифметический тип, либо быть указателем. Тип результата — int А 7.15. Оператор логического ИЛИ логическое-ИЛИ-выражение: логическое-И-выражение логическое-ИЛИ-выражение || логическое-И-выражение Операторы || выполняются слева направо. Оператор || выдает 1, если по крайней мере один из операндов не равен нулю, и 0 в противном случае. В отличие от | , оператор || гарантирует, что вычисления будут проводиться слева направо: вычисляется первый операнд, включая все побочные эффекты; если он не равен 0, то значение выражения есть 1. В противном случае вычисляется правый операнд, и если он не равен 0, то значение выражения есть 1, в противном случае оно равно 0. Операнды могут принадлежать разным типам, но операнд должен иметь либо арифметический тип, либо быть указателем. Тип результата — int А 7.16. Условный оператор условное-выражение: логическое-ИЛИ-выражение логическое-ИЛИ-выражение ? выражение : условное-выражение Вычисляется первое выражение, включая все побочные эффекты; если оно не равно 0, то результат есть значение второго выражения, в противном случае — значение третьего выражения. Вычисляется только один из двух последних операндов: второй или третий. Если второй и третий операнды арифметические, то выполняются обычные арифметические преобразования, приводящие к некоторому общему типу, который и будет типом результата. Если оба операнда имеют тип void , или являются структурами или объединениями одного и того же типа, или представляют собой указатели на объекты одного и того же типа, то результат будет иметь тот же тип, что и операнды. Если один из операндов имеет тип "указатель", а другой является константой 0, то 0 приводится к типу "указатель", этот же тип будет иметь и результат. Если один операнд является указателем на void , а второй — указателем другого типа, то последний преобразуется в указатель на void , который и будет типом результата. При сравнении типов указателей квалификаторы типов (А8.2) объектов, на которые указатели ссылаются, во внимание не принимаются, но тип результата наследует квалификаторы обеих ветвей условного выражения. А 7.17. Выражения присваивания Существует несколько операторов присваивания; они выполняются справа налево. выражение-присваивания: условное-выражение унарное-выражение оператор-присваивания выражение-присваивания оператор-присваивания: один из /= %= += -= <<= >>= &= ^= |= Операторы присваивания в качестве левого операнда требуют lvalue, причем модифицируемого; это значит, что оно не может быть массивом, или иметь незавершенный тип, или быть функцией. Тип левого операнда, кроме того, не может иметь квалификатора const ; и, если он является структурой или объединением, в них не должно быть элементов или подэлементов (для вложенных структур или объединений) с квалификаторами const Тип выражения присваивания соответствует типу его левого операнда, а значение равно значению его левого операнда после завершения присваивания. В простом присваивании с оператором = значение выражения замещает объект, к которому обращается lvalue. При этом должно выполняться одно из следующих условий: оба операнда имеют арифметический тип (если типы операндов разные, правый операнд приводится к типу левого операнда); оба операнда есть структуры или объединения одного и того же типа; один операнд есть указатель, а другой — указатель на void ; левый операнд — указатель, а правый — константное выражение со значением 0; оба операнда — указатели на функции или объекты, имеющие одинаковый тип (за исключением возможного отсутствия const или volatile у правого операнда). Выражение Е1 ор = Е2 эквивалентно выражению Е1 = Е1 ор(Е2) с одним исключением: Е1 вычисляется только один раз. А 7.18. Оператор запятая выражение: выражение-присваивания выражение , выражение-присваивания Два выражения, разделенные запятой, вычисляются слева направо, и значение левого выражения отбрасывается. Тип и значение результата совпадают с типом и значением правого операнда. Вычисление всех побочных эффектов левого операнда завершается перед началом вычисления правого операнда. В контексте, в котором запятая имеет специальное значение, например в списках аргументов функций (А7.3.2) или в списках инициализаторов (А8.7) (здесь в качестве синтаксических единиц фигурируют выражения присваивания), оператор запятая может появиться только в группирующих скобках. Например, в f(a, (t=3, t+2), с) три аргумента, из которых второй имеет значение 5. А 7.19. Константные выражения Синтаксически, константное выражение — это выражение с ограниченным подмножеством операторов: константное-выражение: условное-выражение При указании case -меток в переключателе, задании границ массивов и длин полей битов, на месте значений перечислимых констант и инициализаторов, а также в некоторых выражениях для препроцессора требуются выражения, вычисление которых приводит к константе. Константные выражения не могут содержать присваиваний, операторов инкрементирования и декрементирования, вызовов функций и операторов-запятых; перечисленные ограничения не распространяются на операнд оператора sizeof . Если требуется получить целочисленное константное выражение, то его операнды должны состоять из целых, перечислимых ( enum ), символьных констант и констант с плавающей точкой; операции приведения должны специфицировать целочисленный тип, а любая константа с плавающей точкой — приводиться к целому. Из этого следует, что в константном выражении не может быть массивов, операций косвенного обращения (раскрытия указателя), получения адреса и доступа к полям структуры. (Однако для sizeof возможны операнды любого вида.) Для константных выражений в инициализаторах допускается большая свобода; операндами могут быть константы любого типа, а к внешним или статическим объектам и внешним и статическим массивам, индексируемым константными выражениями, возможно применять унарный оператор & . Унарный оператор & может также неявно присутствовать при использовании массива без индекса или функции без списка аргументов. Вычисление инициализатора должно давать константу или адрес ранее объявленного внешнего или статического объекта плюс-минус константа. Меньшая свобода допускается для целочисленных константных выражений, используемых после #if : не разрешаются sizeof -выражения, константы типа enum и операции приведения типа. (См. А12.5.) А 8. Объявления То, каким образом интерпретируется каждый идентификатор, специфицируется объявлениями; они не всегда резервируют память для описываемых ими идентификаторов. Объявления, резервирующие память, называются определениями и имеют следующий вид: объявление: спецификаторы-объявления список-инициализаторов-объявителей необ Объявители в списке-инициализаторов-объявителей содержат объявляемые идентификаторы; спецификаторы-объявления представляют собой последовательности, состоящие из спецификаторов типа и класса памяти. спецификаторы-объявления: спецификатор-класса-памяти спецификаторы-объявления необ спецификатор-типа спецификаторы-объявления необ квалификатор-типа спецификаторы-объявления необ список-инициализаторов-объявителей: инициализатор-объявитель список-инициализаторов-объявителей , инициализатор-объявитель инициализатор-объявитель: объявитель объявитель = инициализатор Объявители содержат подлежащие объявлению имена. Мы рассмотрим их позже, в А8.5. Объявление должно либо иметь по крайней мере один объявитель, либо его спецификатор типа должен определять тег структуры или объединения, либо задавать элементы перечисления; пустое объявление недопустимо. А 8.1. Спецификаторы класса памяти Класс памяти специфицируется следующим образом: спецификатор-класса-памяти: auto register static extern typedef Смысл классов памяти обсуждался в А4. Спецификаторы auto и register дают объявляемым объектам класс автоматической памяти, и эти спецификаторы можно применять только внутри функции. Объявления с auto и register одновременно являются определениями и резервируют память. Спецификатор register эквивалентен auto , но содержит подсказку, сообщающую, что в программе объявленные им объекты используются интенсивно. На регистрах может быть размещено лишь небольшое число объектов, причем определенного типа; указанные ограничения зависят от реализации. В любом случае к register -объекту нельзя применять (явно или неявно) унарный оператор & Новым является правило, согласно которому вычислять адрес объекта класса register нельзя, а класса auto можно. Спецификатор static дает объявляемым объектам класс статической памяти, он может использоваться и внутри, и вне функций. Внутри функции этот спецификатор вызывает выделение памяти и служит определением; его роль вне функций будет объяснена в А11.2. Объявление со спецификатором extern , используемое внутри функции, объявляет, что для объявляемого объекта где-то выделена память; о ее роли вне функций будет сказано в А11.2. Спецификатор typedef не резервирует никакой памяти и назван спецификатором класса памяти из соображений стандартности синтаксиса; речь об этом спецификаторе пойдет в А8.9. Объявление может содержать не более одного спецификатора класса памяти. Если он в объявлении отсутствует, то действуют следующие правила: считается, что объекты, объявляемые внутри функций, имеют класс auto ; функции, объявляемые внутри функций, — класс extern ; объекты и функции, объявляемые вне функций, — статические и имеют внешние связи (см. А10, А11). А 8.2. Спецификаторы типа Спецификаторы типа определяются следующим образом: спецификатор-типа: void char short int long float double signed unsigned структуры-или-объединения-спецификатор спецификатор-перечисления typedef-имя Вместе с int допускается использование еще какого-то одного слова — long или short ; причем сочетание long int имеет тот же смысл, что и просто long ; аналогично short int — то же самое, что и short Слово long может употребляться вместе с double . С int и другими его модификациями ( short , long или char ) разрешается употреблять одно из слов signed или unsigned . Любое из последних может использоваться самостоятельно, в этом случае подразумевается int Спецификатор signed бывает полезен, когда требуется обеспечить, чтобы объекты типа char имели знак; его можно применять и к другим целочисленным типам, но в этих случаях он избыточен. За исключением описанных выше случаев объявление не может содержать более одного спецификатора типа. Если в объявлении нет ни одного спецификатора типа, то имеется в виду тип int Для указания особых свойств объявляемых объектов предназначаются квалификаторы: квалификатор-типа: const volatile Квалификаторы типа могут употребляться с любым спецификатором типа. Разрешается инициализировать const -объект, однако присваивать ему что-либо в дальнейшем запрещается. Смысл квалификатора volatile зависит от реализации. Средства const и volatile (изменчивый) введены стандартом ANSI. Квалификатор const применяется, чтобы разместить объекты в памяти, открытой только на чтение (ПЗУ), или чтобы способствовать возможной оптимизации. Назначение квалификатора volatile — подавить оптимизацию, которая без этого указания могла бы быть проведена. Например, в машинах, где адреса регистров ввода-вывода отображены на адресное пространство памяти, указатель на регистр некоторого устройства мог бы быть объявлен как volatile , чтобы запретить компилятору экономить очевидно избыточную ссылку через указатель. Компилятор может игнорировать указанные квалификаторы, однако обязан сигнализировать о явных попытках изменить значение const - объектов. |