Р.У. Себеста - Основные копцепции языков программирования (2001) (1160794), страница 75
Текст из файла (страница 75)
пока не будет выполнено сложение во взятом в скобки подвыражении. 281 б.2. Арифметические выражения Языки. допускающие использование скобок в арифметических выражениях. могут обхолиться вообще без правил приоритетов и просто размешать все операторы слева направо или справа налево. Программист сам укажет требуемый порялок вычисления, использовав для этого скобки.
Это было бы даже проще. поскольку ни автору программы. ни ее читателю не потребовалось бы помнить все правила приоритетов или ассоциативности. Недостатком такой схемы является утомительность написания выражений и возьюжность серьезного ухудшения читабельности. Все же. Кеном Айверсоном, разработ1нком языка АР).. такой выбор был сделан. 6.2. 1.4.
Условные выражения На ланный момент мы завершили обсуждение унарных и бинарных операторов. Рассмотрим теперь тернарный оператор?:, являющийся частью языков С, С и )ака. Этот оператор используется для создания условных выражений. Иногда лля выполнения условного присваивания используется операторная структура йй-Е)ьвп-в1вв. Рассмотрим следующее выражение: йх' (ссспс = 0) капп ачегагзе: = 0 е1ве ачегасе : вцж / соспс В языках С, С~-+ и )ача это можно выразить с помощью следующего оператора присваивания, использующего условное выражение: выражение 1 ? выражение 2 : выражение 3 Здесь выражение 1 интерпретируется как булевское выражение.
Если это выражение будет вычислено как истинное, то всему выражению будет присвоен результат выражения 2; в противном случае — результат выражения 3. Используя условное выражение, мы можем следующим образом переписать приведенный выше блок команд дк'-сЬеп-е1ввк ачегасе = (соцпТ == 0) ? 0 : виж / соспс) По сути, знак вопроса отмечает начало оператора с)звп, а двоеточие — начало оператора е1яе. Использование обоих операторов обязательно. Отметим также, что знак ? использован в условном выражении как тернарный оператор. Условные выражения могут использоваться в тех же местах программ на языках С, Сь н )ача, что и другие выражения. 6.2.2. Порядок вычиспенив операндов Такая структурная характеристика, как порядок вычисления операндов, обычно освещается не очень широко. Процесс вычисления значений переменных, входящих в выражения, происходит путем выборки их из памяти. Константы также иногда вычисляются тем же способом.
В других случаях константа может являться частью команды на машинном языке и не требовать выборки из памяти. Если операнд представляет собой выражение, заключенное в скобки, то прежде, чем он сможет использоваться, все солержашиеся в ием операнды должны быть вычислены. Если ни один из операндов в операторе не имеет побочных эффектов, то порядок вычисления операнлов несуществен. Слеловательно, особый интерес представляет именно наличие у операндов побочных эффектов.
282 Глава б. Выражения н операторы присваивания 6.2.2.1. Побочные эффекпи Побочный эффект (зЫе ейесг) функции, называемый функциональным побочным эффектом (Йпс()опа! зЫе ейес(), возникает при изменении функцией олного из своих параметров или глобачьной переменной. (Напомним. что глобальная переменная объявляется вне функции, но доступна в ней.) Рассмотрим слелуюшее выражение: 80)((А) Если функция Г0)! не вызывает побсчного эффекта, связанного с изменением переменной А. то порядок вычисления двух операндов, переменной А и функции Г0И(А) на зна:ение выражения не влияет. Если же функция Г0Ы изменяет значение переменной А.
то юрялок вычисления становится существенным. Рассмотрим следующую ситуацию: :/) нкция Г()Н возвращает значение аргумента, деленное на 2, и присваивает своему параметру значение 20. Предположим далее, что у нас имеются следующие команлы: А:= 10) 8:= А + Гцн(А) Если в процессе вычисления приведенного выше выражения переменная А выбирается нз памяти первой, то ее значение равно 10, а значение самого выражения равно 15. Однако если первым вычисляется второй операнд выражения, то значение первого уже становится равным 20, а значение всего выражения — 25. Ниже приводится пример программы на языке С, иллюстрирующей ту же проблему .шя случая изменения функцией глобальной переменной, входящей в выражение. 1пс а = 5; дпк гцп1() — 17; кеццкп 3; ) /* конец функции йцп1 */ чозгз бцп2() а = а + Гцп1()) ) /* конец функции Гцп2 */ чоха ва1п() гцп2() г /* конец функции жазп */ Значение.
вычисляемое в функции гцп2 для переменной а, зависит от порядка выч|клсння операндов выражения а + 1цп1 (), поэтому возможны два значения переменной а:8и20. Существуют два возможных решения проблемы, связанной с определением порядка вычисления операндов. Во-первых, разработчики языка могут запретить во)можность воздействия функции на величину выражения, просто не допуская функционального побочного эффелта. Второй метод борьбы с этой проблемой — указать в определении языка точный порядок вычисления операндов выражений и потребовать от разработчиков средств реализации языка придерживаться именно этою порядка.
Запретить функциональный побочный эффект трудно, и такой подход уничтожает некоторую гибкость программирования. Рассмотрим языки С и С++, состоящие исключительно из функций. Для того чтобы запретить побочные эффекты двусторонних параметров и при этом по-прежнему иметь возможность создавать подпрограммы, возвра- 6.2. Арифметические выражения шаюшие несколько значений, потребуется новый тип подпрограмм, подобных процедурам в других императивных языках программирования. Доступ из функций к глобальным переменным также можно запретить.
Впрочем, когда важна эффективность, использование доступа к глобальным переменным для того, чтобы избежать передачи параметров, является важным методом увеличения скорости выполнения программ. В компиляторах, например, глобальный доступ к таким данным, как таблица идентификаторов, используется широко. Проблема, связанная с наличием строгого порядка вычислений, состоит в том, что некоторые используемые компиляторами методики оптимизации программ содержат вычисление переупорядоченных операндов. Если же строго задать порядок вычисления, то эти методы оптимизации, выполняемые при вызовах функций, будут не доступны. Следовательно.
оптимального решения не существует, что и подтверждается существующими языками программирования. Третье решение проблемы представили разработчики языка гОКТйАХ 77. В описании этого языка указывается, что выражения, содержащие вызовы функций. дозволены только тогла, когда функции не меняют значения операндов выражений. К сожалению, компилятору не так просто определить точно, какое влияние имеет функция на внешние переменные, особенно если присутствие глобальных переменных обеспечивается оператором СОММОН.
а совмещение имен — оператором Е001ЧАЬЕМСЕ. Это как раз тот случай, когда определение языка указывает условия, при которых конструкция дозволена, но лействительное существование в программе такой конструкции должен обеспечивать программист. Языки Рааса! и Аба позволяют операндам бинарных операций вычисляться в любом порядке.
выбираемом разработчиком средств реализации языка. Более того, функции этих языков могут иметь побочные эффекты, что порождает описанные ранее проблемы. Обсуждение функциональных побочных эффектов мы продолжим в главе 8. Описание языка )ача обеспечивает вычисление операндов в порядке слева направо, снимая, таким образом, проблему, рассмотренную в данном разделе. 6.3. Перегруженные операторы Арифметические операции часто используются для достижения нескольких целей. Знак э. например, часто используется лля сложения любых операндов числовых типов. Неко~ орые языки, например 3ача, используют его еше и для конкатенашяи строк. Такое множественное использование операторов получило название перегрузки операторов (орегаюг очег!оайп8), и считается приемлемым, если не врелит читабельности и/или належности.
Некоторые считают, например, что в языках АРЕ и ЯЧОВО(. операторы слишком перегружены, поскольку они используются в бинарных и унарных операциях. В качестве примера перегрузки рассмотрим использование амперсанда (знака ь) в языке С. Как бинарный оператор он задает операцию побитового логического И. Его значение как унарного оператора существенно отличается от предыдущего и представляет собой адрес переменной, используемой в качестве операнда. В этом случае амперсанд называется оператором вычисления адреса. Например, выполнение присваивания х = ьуз приведет к помещению адреса переменной у в переменную х.
При таком множественном использовании амперсанда возникают две проблемы. Во-первых, использование од- 284 Главе 6. Выражения и операторы присваивания ;; чвола для двух абсолютно несвязанных операций вредит читабельности. Во. простая ошибка при наборе или потеря первого операнда операции побитового .:кого И могут пройти незамеченными компилятором, поскольку амперсанд будет .. -.-=ч кзк оператор вычисления адреса. Обнаружить такую ошибку довольно сложно. -:кэнчески все языки программирования содержат менее серьезные, но сходные . чы.