Приоритеты операций в си. Выражения и операции

04.04.2019

Требования к вызываемым функциям

Чтобы определяемую программистом функцию PL/SQL можно было вызывать из команд SQL , она должна отвечать следующим требованиям:

  • Все параметры функции должны иметь режим использования IN . Режимы IN OUT и OUT в функциях , встраиваемых в SQL-код, недопустимы.
  • Типы данных параметров функций и тип возвращаемого значения должны распоз­наваться сервером Oracle. PL/SQL дополняет основные типы Oracle, которые пока не поддерживаются базой данных. Речь идет о типах BOOLEAN , BINARY_INTEGER , ассо­циативных массивах, записях PL/SQL и определяемых программистом подтипах.
  • Функция должна храниться в базе данных. Функция, определенная на стороне клиента, не может вызываться в командах SQL, так как SQL не сможет разрешить ссылку на эту функцию.

По умолчанию пользовательские функции, вызываемые в SQL , оперируют данными одной строки, а не столбца (как агрегатные функции SUM , MIN и AVG). Чтобы соз­дать агрегатные функции, вызываемые в SQL , необходимо использовать интерфейс ODCIAggregate , который является частью среды Oracle Extensibility Framework . За подробной информацией по этой теме обращайтесь к документации Oracle.

Ограничения для пользовательских функций, вызываемых в SQL

С целью защиты от побочных эффектов и непредсказуемого поведения хранимых про­цедур Oracle не позволяет им выполнять следующие действия:

  • Хранимые функции не могут модифицировать таблицы баз данных и выполнять команды DDL (CREATE TABLE , DROP INDEX и т. д.), INSERT , DELETE , MERGE и UPDATE . Эти ограничения ослабляются, если функция определена как автономная транзакция . В таком случае любые вносимые ею изменения осуществляются не­зависимо от внешней транзакции, в которой выполняется запрос.
  • Хранимые функции, которые вызываются удаленно или в параллельном режиме, не могут читать или изменять значения переменных пакета. Сервер Oracle не поддер­живает побочные эффекты, действие которых выходит за рамки сеанса пользователя.
  • Хранимая функция может изменять значения переменных пакета, только если она вызывается в списке выборки либо в предложении VALUES или SET . Если хранимая функция вызывается в предложении WHERE или GROUP BY , она не может изменять значения переменных пакета.
  • До выхода Oracle8 пользовательские функции не могли вызывать процедуру RAISE_ APPLICATION_ERROR .
  • Хранимая функция не может вызывать другой модуль (хранимую процедуру или функцию), не соответствующий приведенным требованиям.
  • Хранимая функция не может обращаться к представлению, которое нарушает любое из предшествующих правил. Представлением (view) называется хранимая команда SELECT , в которой могут вызываться хранимые функции.
  • До выхода Oracle11g для передачи параметров функциям могла использоваться только позиционная запись. Начиная с Oracle11g, допускается передача параметров по имени и смешанная запись.

Непротиворечивость чтения и пользовательские функции

Модель непротиворечивости чтения в базе данных Oracle проста и понятна: после выполнения запрос «видит» данные в том состоянии, в котором они существовали (были зафиксированы в базе данных) на момент начала запроса, с учетом результатов изменений, вносимых командами DML текущей транзакции. Таким образом, если мой запрос был выполнен в 9:00 и продолжает работать в течение часа, даже если за это время другой пользователь внесет в данные изменения, они не отразятся в моем запросе.

Но если не принять специальных мер предосторожности с пользовательскими функциями в ваших запросах, может оказаться, что ваш запрос будет нарушать (по крайней мере на первый взгляд) модель непротиворечивости чтения базы данных Oracle. Чтобы понять этот аспект, рассмотрим следующую функцию и запрос, в котором она вызывается:

FUNCTION total_sales (id_in IN account.account_id%TYPE) RETURN NUMBER IS CURSOR tot_cur IS SELECT SUM (sales) total FROM orders WHERE account_id = id_in AND TO_CHAR (ordered_on, "YYYY") = TO_CHAR (SYSDATE, "YYYY"); tot_rec tot_cur%ROWTYPE; BEGIN OPEN tot_cur; FETCH tot_cur INTO tot_rec; CLOSE tot_cur; RETURN tot_rec.total; END; SELECT name, total_sales (account_id) FROM account WHERE status = "ACTIVE";

Таблица account содержит 5 миллионов активных строк, а таблица orders - 20 миллио­нов. Я запускаю запрос в 10:00, на его завершение уходит около часа. В 10:45 приходит некто, обладающий необходимыми привилегиями, удаляет все строки из таблицы orders и закрепляет транзакцию. По правилам модели непротиворечивости чтения Oracle сеанс, в котором выполняется запрос, не должен рассматривать эти строки как удаленные до завершения запроса. Но при следующем вызове из запроса функция total_sales не найдет ни одной строки и вернет NULL - и так будет происходить до завершения запроса.

При выполнении запросов из функций, вызываемых в коде SQL, необходимо внимательно следить за непротиворечивостью чтения. Если эти функции вызываются в продолжитель­ных запросах или транзакциях, вероятно, вам стоит выполнить следующую команду для обеспечения непротиворечивости чтения между командами SQL текущей транзакции: SET TRANSACTION READ ONLY

В этом случае необходимо позаботиться о наличии достаточного табличного простран­ства отмены.

Определение подпрограмм PL/SQL в командах SQL (12.1 и выше)

Разработчики уже давно могли вызывать свои функции PL/SQL из команд SQL . До­пустим, я создал функцию с именем BETWNSTR , которая возвращает подстроку с заданной начальной и конечной позицией:

FUNCTION betwnstr (string_in IN VARCHAR2 , start_in IN PLS_INTEGER , end_in IN PLS_INTEGER) продолжение # RETURN VARCHAR2 IS BEGIN RETURN (SUBSTR (string_in, start_in, end_in - start_in + 1)); END;

Функция может использоваться в запросах следующим образом:

SELECT betwnstr (last_name, 3, 5) FROM employees

Эта возможность позволяет «расширить» язык SQL функциональностью, присущей конкретному приложению, и повторно использовать алгоритмы (вместо копирования). К недостаткам выполнения пользовательских функций в SQL следует отнести необ­ходимость переключения контекста между исполнительными ядрами SQL и P L/SQL . Начиная с Oracle Database 12c вы можете определять функции и процедуры PL/SQL в секции WITH подзапроса, чтобы затем использовать их как любую встроенную или пользовательскую функцию. Эта возможность позволяет консолидировать функцию и запрос в одной команде:

WITH FUNCTION betwnstr (string_in IN VARCHAR2, start_in IN PLS_INTEGER, end_in IN PLS_INTEGER) RETURN VARCHAR2 IS BEGIN RETURN (SUBSTR (string_in, start_in, end_in - start_in + 1)); END; SELECT betwnstr (last_name, 3, 5) FROM employees

Главное преимущество такого решения - повышение производительности, так как при таком определении функций затраты на переключение контекста с ядра SQL н а ядро PL/SQL существенно снижаются. С другой стороны, за него приходится расплачиваться возможностью повторного использования логики в других частях приложения.

Впрочем, для определения функций в секции WITH есть и другие причины. В SQL можно вызвать пакетную функцию, но нельзя сослаться на константу, объявленную в пакете (если только команда SQL не выполняется внутри блока PL/SQL), как показано в сле­дующем примере:

SQL> CREATE OR REPLACE PACKAGE pkg 2 IS 3 year_number CONSTANT INTEGER:= 2013; 4 END; 5 / Package created. SQL> SELECT pkg.year_number FROM employees 2 WHERE employee_id = 138 3 / SELECT pkg.year_number FROM employees * ERROR at line 1: ORA-06553: PLS-221: "YEAR_NUMBER" is not a procedure or is undefined

Классическое обходное решение основано на определении функции в пакете и ее по­следующем вызове:

SQL> CREATE OR REPLACE PACKAGE pkg 2 IS 3 FUNCTION year_number 4 RETURN INTEGER; 5 END; 6 / Package created. SQL> CREATE OR REPLACE PACKAGE BODY pkg 2 IS 3 c_year_number CONSTANT INTEGER:= 2013; 4 5 FUNCTION year_number 6 RETURN INTEGER 7 IS 8 BEGIN 9 RETURN c_year_number; 10 END; 11 END; 12 / Package body created. SQL> SELECT pkg.year_number 2 FROM employees 3 WHERE employee_id = 138 4 / YEAR NUMBER ------------ 2013

Для простого обращения к значению константы в команде SQL потребуется слишком много кода и усилий. Начиная с версии 12.1 это стало излишним - достаточно создать функцию в секции WITH:

WITH FUNCTION year_number RETURN INTEGER IS BEGIN RETURN pkg.year_number; END; SELECT year_number FROM employees WHERE employee_id = 138

Функции PL/SQL , определяемые в SQL, также пригодятся при работе с автономными базами данных, доступными только для чтения. Хотя в таких базах данных невозмож­но создавать «вспомогательные» функции PL/SQL , вы можете определять их прямо в запросах.

Механизм WITH FUNCTION стал чрезвычайно полезным усовершенствованием языка SQL. Тем не менее каждый раз, когда вы планируете его использование, стоит задать себе один вопрос: «Потребуется ли эта функциональность в нескольких местах приложения?»

Если вы ответите на него положительно, следует решить, компенсирует ли выигрыш по производительности от применения WITH FUNCTION потенциальные потери от копи­рования и вставки этой логики в нескольких командах SQL.

Учтите, что в версии 12.1 в блоках PL/SQL невозможно выполнить статическую коман­ду select с секцией with function . Безусловно, это выглядит очень странно, и я уверен, что в 12.2 такая возможность появится, но пока при попытке выполнения следующего кода будет выдана ошибка:

SQL> BEGIN 2 WITH FUNCTION full_name (fname_in IN VARCHAR2, lname_in IN VARCHAR2) 3 RETURN VARCHAR2 4 IS 5 BEGIN 6 RETURN fname_in || " " || lname_in; 7 END; 8 9 SELECT LENGTH (full_name (first_name, last_name)) 10 INTO c 11 FROM employees; 12 13 DBMS_OUTPUT.put_line ("count = " || c); 14 END; 15 / WITH FUNCTION full_name (fname_in IN VARCHAR2, lname_in IN VARCHAR2) * ERROR at line 2: ORA-06550: line 2, column 18: PL/SQL: ORA-00905: missing keyword

Помимо конструкции WITH FUNCTION, в версии 12.1 также появилась директива UDF для улучшения быстродействия функций PL/SQL, выполняемых из SQL.

The precedence and associativity of C operators affect the grouping and evaluation of operands in expressions. An operator"s precedence is meaningful only if other operators with higher or lower precedence are present. Expressions with higher-precedence operators are evaluated first. Precedence can also be described by the word "binding." Operators with a higher precedence are said to have tighter binding.

The following table summarizes the precedence and associativity (the order in which the operands are evaluated) of C operators, listing them in order of precedence from highest to lowest. Where several operators appear together, they have equal precedence and are evaluated according to their associativity. The operators in the table are described in the sections beginning with Postfix Operators . The rest of this section gives general information about precedence and associativity.

Precedence and Associativity of C Operators

Symbol 1 Type of Operation Associativity
() . ->

++ -- (postfix)

Expression Left to right
sizeof & * + - ~ !

++ -- (prefix)

Unary Right to left
typecasts Unary Right to left
* / % Multiplicative Left to right
+ - Additive Left to right
<< >> Bitwise shift Left to right
< > <= >= Relational Left to right
== != Equality Left to right
& Bitwise-AND Left to right
^ Bitwise-exclusive-OR Left to right
| Bitwise-inclusive-OR Left to right
&& Logical-AND Left to right
|| Logical-OR Left to right
? : Conditional-expression Right to left
= *= /= %=

+= -= <<= >>= &=

^= |=

Simple and compound assignment 2 Right to left
, Sequential evaluation Left to right

    Operators are listed in descending order of precedence. If several operators appear on the same line or in a group, they have equal precedence.

    All simple and compound-assignment operators have equal precedence.

An expression can contain several operators with equal precedence. When several such operators appear at the same level in an expression, evaluation proceeds according to the associativity of the operator, either from right to left or from left to right. The direction of evaluation does not affect the results of expressions that include more than one multiplication (* ), addition (+ ), or binary-bitwise (& , | , or ^ ) operator at the same level. Order of operations is not defined by the language. The compiler is free to evaluate such expressions in any order, if the compiler can guarantee a consistent result.

Only the sequential-evaluation (, ), logical-AND (&& ), logical-OR (|| ), conditional-expression (? : ), and function-call operators constitute sequence points and therefore guarantee a particular order of evaluation for their operands. The function-call operator is the set of parentheses following the function identifier. The sequential-evaluation operator (, ) is guaranteed to evaluate its operands from left to right. (Note that the comma operator in a function call is not the same as the sequential-evaluation operator and does not provide any such guarantee.) For more information, see Sequence Points .

Logical operators also guarantee evaluation of their operands from left to right. However, they evaluate the smallest number of operands needed to determine the result of the expression. This is called "short-circuit" evaluation. Thus, some operands of the expression may not be evaluated. For example, in the expression

the second operand, y++ , is evaluated only if x is true (nonzero). Thus, y is not incremented if x is false (0).

Examples

The following list shows how the compiler automatically binds several sample expressions:

Expression Automatic Binding
a & b || c (a & b) || c
a = b || c a = (b || c)
q && r || s-- (q && r) || s--

In the first expression, the bitwise-AND operator (& || ), so a & b forms the first operand of the logical-OR operation.

In the second expression, the logical-OR operator (|| ) has higher precedence than the simple-assignment operator (= ), so b || c is grouped as the right-hand operand in the assignment. Note that the value assigned to a is either 0 or 1.

The third expression shows a correctly formed expression that may produce an unexpected result. The logical-AND operator (&& ) has higher precedence than the logical-OR operator (|| ), so q && r is grouped as an operand. Since the logical operators guarantee evaluation of operands from left to right, q && r is evaluated before s-- . However, if q && r evaluates to a nonzero value, s-- is not evaluated, and s is not decremented. If not decrementing s would cause a problem in your program, s-- should appear as the first operand of the expression, or s should be decremented in a separate operation.

The following expression is illegal and produces a diagnostic message at compile time:

Illegal Expression Default Grouping
p == 0 ? p += 1: p += 2 (p == 0 ? p += 1: p) += 2

In this expression, the equality operator (== ) has the highest precedence, so p == 0 is grouped as an operand. The conditional-expression operator (? : ) has the next-highest precedence. Its first operand is p == 0 , and its second operand is p += 1 . However, the last operand of the conditional-expression operator is considered to be p rather than p += 2 , since this occurrence of p binds more closely to the conditional-expression operator than it does to the compound-assignment operator. A syntax error occurs because += 2 does not have a left-hand operand. You should use parentheses to prevent errors of this kind and produce more readable code. For example, you could use parentheses as shown below to correct and clarify the preceding example.

Аннотация: Рассматривается синтаксис и семантика построения выражений языка С#. Рассматриваются все возможные операции языка, их приоритеты. При рассмотрении логических операций обсуждается работа со шкалами. Рассмотрен лямбда оператор и лямбда выражение. Большое внимание уделяется преобразованиям типа данных при вычислении выражений. Обсуждаются вопросы эффективного вычисления выражений. Предлагаются задачи на эту тему.

Проект к данной лекции Вы можете скачать .

Выражения

Выражения строятся из операндов - констант, переменных, функций, - объединенных знаками операций и скобками. При вычислении выражения определяется его значение и тип. Эти характеристики выражения однозначно определяются значениями и типами операндов, входящих в выражение , и правилами вычисления выражения. Правила задают:

  • приоритет операций,
  • для операций одного приоритета порядок применения - слева направо или справа налево;
  • преобразование типов операндов и выбор реализации для перегруженных операций;
  • тип и значение результата выполнения операции над заданными значениями операндов определенного типа.

Приоритет и порядок выполнения операций

Большинство операций в языке C#, их приоритет и порядок наследованы из языка C++. Однако имеются и различия: например, нет операции " , " , позволяющей вычислять список выражений; добавлены операции checked и unchecked , применимые к выражениям.

Как это обычно делается, приведем таблицу приоритетов операций, в каждой строке которой собраны операции одного приоритета, а строки следуют в порядке приоритетов, от высшего к низшему.

Таблица 3.1. Приоритеты операций языка C#
Приоритет Категория Операции Порядок
0 Первичные (expr), x.y, x->y, f(x), a[x], x++, x--, new, typeof(t), checked(expr), unchecked(expr) Слева направо
1 Унарные +, -, !, ~, ++x, --x, (T)x, sizeof(t) Слева направо
2 Мультипликативные (Умножение) *, /, % Слева направо
3 Аддитивные (Сложение) +, - Слева направо
4 Сдвиг << ,>> Слева направо
5 Отношения, проверка типов <, >, <=, >=, is, as Слева направо
6 Эквивалентность ==, != Слева направо
7 Логическое И (AND) & Слева направо
8 Логическое исключающее ИЛИ (XOR) ^ Слева направо
9 Логическое ИЛИ (OR) | Слева направо
10 Условное логическое И && Слева направо
11 Условное логическое ИЛИ || Слева направо
12 Условное выражение ? : Справа налево
13 Присваивание

Склеивание с null

=, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |= Справа налево
14 Лямбда-оператор => Справа налево

Перегрузка операций и методов

Под перегрузкой операции понимается существование нескольких реализаций одной и той же операции. Например, операция со знаком "+" выполняется по-разному в зависимости от того, являются ли ее операнды целыми числами, длинными целыми, целыми с фиксированной или плавающей точкой или строками текста.

Нужно понимать, что операции - это частный случай записи методов класса. Методы класса, так же как и операции, могут быть перегружены. Метод класса называется перегруженным , если существует несколько реализаций этого метода. Перегруженные методы имеют одно и то же имя, но должны отличаться своей сигнатурой . Сигнатуру метода составляет список типов формальных аргументов метода. Так что два метода класса с одним именем, но отличающиеся, например, числом параметров, имеют разную сигнатуру и удовлетворяют требованиям, предъявляемым к перегруженным методам.

Большинство операций языка C# перегружены - одна и та же операция может применяться к операндам различных типов. Поэтому прежде чем выполнять операцию, проводится поиск реализации, подходящей для данных типов операндов. Замечу, что операции, как правило, выполняются над операндами одного типа. Если же операнды разных типов, то предварительно происходит неявное преобразование типа одного из операндов. Оба операнда могут быть одного типа, но преобразование типов может все равно происходить - по той причине, что для заданных типов нет соответствующей перегруженной операции. Такая ситуация достаточно часто возникает на практике, поскольку, например, операция сложения не определена для младших подтипов арифметического типа. Если для данных типов операндов нет подходящей реализации операции и невозможно неявное приведение типов операндов, то, как правило, эта ошибка обнаруживается еще на этапе компиляции.

Преобразования типов

Каждый объект (переменная), каждый операнд при вычислении выражения, само выражение характеризуется парой , задающей значение выражения и его тип. В процессе вычислений зачастую возникает необходимость преобразования типов - необходимость преобразовать пару к паре . Исходная пара называется источником преобразования, заключительная - целью преобразования.

Необходимость в подобных преобразованиях возникает, как уже отмечалось, по ходу вычисления выражения при приведении операндов к типу, согласованному с типом операции. Преобразование типов необходимо в операторах присваивания, когда тип выражения правой части оператора приводится к типу, заданному левой частью этого оператора. Семантика присваивания имеет место и при вызове методов в процессе замены формальных аргументов метода фактическими параметрами. И здесь необходимо преобразование типов.

Преобразования типов можно разделить на безопасные и опасные. Безопасное преобразование - это преобразование, для которого гарантируется, что:

Преобразование, для которого не выполняется хотя бы одно из этих условий, называется опасным. Достаточным условием существования безопасного преобразования является, например, условие того, что тип является подтипом типа . Действительно, в этом случае любое значение источника является одновременно и допустимым значением цели. Так, преобразование от типа int к типу double является безопасным. Обратное преобразование, естественно, будет опасным.

Некоторые преобразования типов выполняются автоматически. Такие преобразования называются неявными, и они часто встречаются при вычислении выражений. Очевидно, что неявными могут быть только безопасные преобразования. Любое опасное преобразования должно явно задаваться самим программистом, который и берет на себя всю ответственность за выполнение опасного преобразования.

Существуют разные способы выполнения явных преобразований - операция кастинга (приведение к типу), методы специального класса Convert , специальные методы ToString , Parse . Все эти способы будут рассмотрены в данной лекции.

Поясним, как выполняются неявные преобразования при вычислении выражения. Пусть при вычислении некоторого выражения необходимо выполнить сложение , где имеет тип double , а - int . Среди многочисленных реализаций сложения есть операции, выполняющие сложение операндов типа int и сложение операндов типа double, так что при выборе любой из этих реализаций сложения потребуется преобразование типа одного из операндов. Поскольку преобразование типа от int к double является безопасным, а в другую сторону это преобразование опасно, то выбирается безопасное преобразование, выполняемое автоматически, второй операнд неявно преобразуется к типу double, выполняется сложение операндов этого типа, и результат сложения будет иметь тип double .

Организация программного проекта ConsoleExpressions

Как обычно, все примеры программного кода, появляющиеся в тексте, являются частью программного проекта. Опишу структуру используемого в этой лекции консольного проекта, названного ConsoleExpressions. Помимо созданного по умолчанию класса Program , в проект добавлены два класса с именами TestingExpressions и Scales . Каждый из методов класса TestingExpressions представляет тест, который позволяет анализировать особенности операций, используемых при построении выражений, так что этот класс представляет собой сборник тестов. Класс Scale носит содержательный характер, демонстрируя работу со шкалами, о которых пойдет речь в этой лекции. Чтобы иметь возможность вызывать методы этих классов, в процедуре Main класса Program объявляются и создаются объекты этих классов. Затем эти объекты используются в качестве цели вызова соответствующих методов. Общая схема процедуры Main и вызова методов класса такова:

static void Main(string args) { string answer = "Да"; do { try { TestingExpressions test = new TestingExpressions(); test.Casting(); //Вызов других методов … } catch (Exception e) { Console.WriteLine("Невозможно нормально продолжить работу!"); Console.WriteLine(e.Message); } Console.WriteLine("Продолжим работу? (Да/нет)"); answer = Console.ReadLine(); } while (answer == "Да" || answer == "да" || answer == "yes"); }

Всякий раз, когда в тексте лекции нужно будет привести пример кода, будет приводиться либо полный текст вызываемого метода, например, метода Casting , либо отдельный фрагмент метода.

C++ для начинающих

4.13. Приоритеты

Приоритеты операций задают последовательность вычислений в сложном выражении. Например, какое значение получит ival?

Int ival = 6 + 3 * 4 / 2 + 2;

Если вычислять операции слева направо, получится 20. Среди других возможных результатов будут 9, 14 и 36. Правильный ответ: 14.
В С++ умножение и деление имеют более высокий приоритет, чем сложение, поэтому они будут вычислены раньше. Их собственные приоритеты равны, поэтому умножение и деление будут вычисляться слева направо. Таким образом, порядок вычисления данного выражения таков:

1. 3 * 4 => 12 2. 12 / 2 => 6 3. 6 + 6 => 12 4. 12 + 2 => 14

Следующая конструкция ведет себя не так, как можно было бы ожидать. Приоритет операции присваивания меньше, чем операции сравнения:

While (ch = nextChar() != "\n")

Программист хотел присвоить переменной ch значение, а затем проверить, равно ли оно символу новой строки. Однако на самом деле выражение сначала сравнивает значение, полученное от nextChar(), с "\n", и результат – true или false – присваивает переменной ch.
Приоритеты операций можно изменить с помощью скобок. Выражения в скобках вычисляются в первую очередь. Например:

4 * 5 + 7 * 2 ==> 34 4 * (5 + 7 * 2) ==> 76 4 * ((5 + 7) * 2) ==> 96

Вот как с помощью скобок исправить поведение предыдущего примера:

While ((ch = nextChar()) != "\n")

Операторы обладают и приоритетом , и ассоциативностью . Оператор присваивания правоассоциативен, поэтому вычисляется справа налево:

Ival = jval = kva1 = lval

Сначала kval получает значение lval, затем jval – значение результата этого присваивания, и в конце концов ival получает значение jval.
Арифметические операции, наоборот, левоассоциативны. Следовательно, в выражении

Ival + jval + kva1 + 1va1

сначала складываются ival и jval, потом к результату прибавляется kval, а затем и lval.
В таблице 4.4 приведен полный список операторов С++ в порядке уменьшения их приоритета. Операторы внутри одной секции таблицы имеют равные приоритеты. Все операторы некоторой секции имеют более высокий приоритет, чем операторы из секций, следующих за ней. Так, операции умножения и деления имеют одинаковый приоритет, и он выше приоритета любой из операций сравнения.

Упражнение 4.18

Каков порядок вычисления следующих выражений? При ответе используйте таблицу 4.4.

(a) ! ptr == ptr->next (b) ~ uc ^ 0377 & ui << 4 (c) ch = buf[ bp++ ] != "\n"

Упражнение 4.19

Все три выражения из предыдущего упражнения вычисляются не в той последовательности, какую, по-видимому, хотел задать программист. Расставьте скобки так, чтобы реализовать его первоначальный замысел.

Упражнение 4.20

Следующие выражения вызывают ошибку компиляции из-за неправильно понятого приоритета операций. Объясните, как их исправить, используя таблицу 4.4.

(a) int i = doSomething(), 0; (b) cout << ival % 2 ? "odd" : "even";

Таблица 4.4. Приоритеты операций

Оператор Значение Использование
:: Глобальная область видимости ::name
:: Область видимости класса class::name
:: Область видимости пространства имен namespace::name
. Доступ к члену object.member
-> Доступ к члену по указателю pointer->member
Взятие индекса variable
() Вызов функции name(expr_list)
() Построение значения type(expr_list)
++ постфиксный инкремент lvalue++
постфиксный декремент lvalue--
typeid идентификатор типа typeid(type)
typeid идентификатор типа выражения typeid(expr)
преобразование типа const_cast(expr)
преобразование типа dynamic_cast(expr)
reinterpret_cast приведение типа reinterpret_cast (expr)
static_cast приведение типа static_cast(expr)
sizeof размер объекта sizeof expr
sizeof размер типа sizeof(type)
++ префиксный инкремент ++lvalue
-- префиксный декремент --lvalue
~ побитовое НЕ ~expr
! логическое НЕ !expr
- унарный минус -expr
+ унарный плюс +expr
* разыменование *expr
& адрес &expr
() приведение типа (type)expr
new выделение памяти new type
new выделение памяти и инициализация new type(exprlist)
new Выделение памяти под массив все формы
delete освобождение памяти все формы
delete освобождение памяти из-под массива все формы
->* доступ к члену классу по указателю pointer-> *pointer_to_member
.* доступ к члену класса по указателю object.*pointer_to_member
* Умножение expr * expr
/ Деление expr / expr
% деление по модулю expr % expr
+ сложение expr + expr
- вычитание expr - expr
<< сдвиг влево expr << expr
>> сдвиг вправо expr >> expr
< меньше expr < expr
<= меньше или равно expr <= expr
> больше expr > expr
>= больше или равно expr >= expr
== равно expr == expr
!= не равно expr != expr
& побитовое И expr & expr
^ побитовое ИСКЛЮЧАЮЩЕЕ ИЛИ expr ^ expr
| побитовое ИЛИ expr | expr
&& логическое И expr && expr
|| логическое ИЛИ expr || expr
?: условный оператор expr ? expr * expr
= присваивание l-значение = expr
=, *=, /=, %=, +=, -=, <<=, >>=, &=, |=, ^= составное присваивание l-значение += expr и т.д.
throw возбуждение исключения throw expr
, запятая expr, expr