Урок №38. Приоритет операций и правила ассоциативности

  Юрий  | 

    | 

  Обновл. 11 Апр 2019  | 

 17997

 ǀ   3 

Чтобы правильно вычислять выражения (например, 4 + 2 * 3), мы должны знать, какие операторы что делают и в каком порядке выполняются. Последовательность, в которой они выполняются, называется приоритетом операций. Следуя обычным правилам математики (в которой умножение следует перед сложением), выражение выше обрабатывается следующим образом: 4 + (2 * 3) = 10.

В C++ все операторы (операции) имеют свой уровень приоритета. Те, в которых он выше, выполняются первыми. В таблице ниже можно увидеть, что приоритет операций умножения и деления (5) выше, чем в операциях сложения и вычитания (6). Компилятор использует приоритет операторов для определения порядка обработки выражений.

А что делать, если у двух операторов в выражении одинаковый уровень приоритета, и они размещены рядом? Какую операцию компилятор выполнит первой? А здесь уже компилятор будет использовать правила ассоциативности, которые указывают направление выполнения операций: слева направо или справа налево. Например, в 3 * 4 / 2, операции умножения и деления имеют одинаковый уровень приоритета (5). А ассоциативность 5 уровня = слева направо, соответственно: (3 * 4) / 2 = 6.

Таблица приоритета и ассоциативности операций

Несколько примечаний:

   1 означает самый высокий уровень приоритета, а 17 — самый низкий. Операции с более высоким уровнем приоритета выполняются первыми.

   L -> R означает слева направо.

   R -> L означает справа налево.

Ассоциативность Оператор Описание Пример
1. Нет :: Глобальная область видимости (унарный) ::name
:: Область видимости класса (бинарный) class_name::member_name
2. L -> R () Круглые скобки (expression)
() Вызов функции function_name(parameters)
() Инициализация type name(expression)
{} uniform инициализация (C++11) type name{expression}
type() Конвертация типа new_type(expression)
type{} Конвертация типа (C++11) new_type{expression}
[] Индекс массива pointer[expression]
. Доступ к члену объекта object.member_name
-> Доступ к члену объекта через указатель object_pointer->member_name
++ Пост-инкремент lvalue++
–– Пост-декремент lvalue––
typeid Информация о типе во время выполнения typeid(type) or typeid(expression)
const_cast Cast away const const_cast(expression)
dynamic_cast Type-checked cast во время выполнения dynamic_cast(expression)
reinterpret_cast Конвертация одного типа в другой reinterpret_cast(expression)
static_cast Type-checked cast во время компиляции static_cast(expression)
3. R -> L + Унарный плюс +expression
Унарный минус -expression
++ Пре-инкремент ++lvalue
–– Пре-декремент ––lvalue
! Логическое НЕ (NOT) !expression
~ Побитовое НЕ (NOT) ~expression
(type) C-style cast (new_type)expression
sizeof Размер в байтах sizeof(type) or sizeof(expression)
& Адрес &lvalue
* Dereference *expression
new Динамическое выделение памяти new type
new[] Динамическое выделение массива new type[expression]
delete Динамическое удаление памяти delete pointer
delete[] Динамическое удаление массива delete[] pointer
4. L -> R ->* Member pointer selector object_pointer->*pointer_to_member
.* Member object selector object.*pointer_to_member
5. L -> R * Умножение expression * expression
/ Деление expression / expression
% Остаток expression % expression
6. L -> R + Сложение expression + expression
Вычитание expression — expression
7. L -> R << Побитовый сдвиг влево expression << expression
>> Побитовый сдвиг вправо expression >> expression
8. L -> R < Сравнение: меньше чем expression < expression
<= Сравнение: меньше чем или равно expression <= expression
> Сравнение: больше чем expression > expression
>= Сравнение: больше чем или равно expression >= expression
9. L -> R == Равно expression == expression
!= Не равно expression != expression
10. L -> R & Побитовое И (AND) expression & expression
11. L -> R ^ Побитовое исключающее ИЛИ (XOR) expression ^ expression
12. L -> R | Побитовое ИЛИ (OR) expression | expression
13. L -> R && Логическое И (AND) expression && expression
14. L -> R || Логическое ИЛИ (OR) expression || expression
15. R -> L ?: Тернарный условный оператор expression ? expression : expression
= Присваивание lvalue = expression
*= Умножение с присваиванием lvalue *= expression
/= Деление с присваиванием lvalue /= expression
%= Деление с остатком и с присваиванием lvalue %= expression
+= Сложение с присваиванием lvalue += expression
-= Вычитание с присваиванием lvalue -= expression
<<= Присваивание с побитовым сдвигом влево lvalue <<= expression
>>= Присваивание с побитовым сдвигом вправо lvalue >>= expression
&= Присваивание с побитовой операцией И (AND) lvalue &= expression
|= Присваивание с побитовой операцией ИЛИ (OR) lvalue |= expression
^= Присваивание с побитовой операцией «исключающее ИЛИ» (XOR) lvalue ^= expression
16. R -> L throw Генерация исключения throw expression
17. L -> R , Оператор Запятая expression, expression

Некоторые операторы вы уже знаете из предыдущих уроков: +, -, *, /, (), =, < и >. Их значения одинаковы как в математике, так и в C++.

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

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

Как возвести число в степень в C++?


Вы уже должны были заметить, что оператор ^, который обычно используется для обозначения возведения в степень в обычной математике, не является таковым в C++. В С++ это побитовая операция XOR. А для возведения числа в степень в C++ используется функция pow(), которая находится в заголовочном файле cmath:

Обратите внимание, параметры и возвращаемые значения функции pow() являются типа double. А поскольку типы с плавающей точкой известны ошибками округления, то результаты pow() могут быть слегка неточными (чуть меньше или чуть больше).

Если вам нужно возвести в степень целое число, то лучше использовать собственную функцию, например:

Не переживайте, если здесь что-то не понятно. Просто помните о проблеме переполнения, которая может произойти, если один из аргументов будет слишком большим.

Тест

Из школьной математики нам известно, что выражения внутри скобок выполняются первыми. Например, в (2 + 3) * 4, часть (2 + 3) выполняется первой.

В этом задании есть 4 выражения, в которых отсутствуют какие-либо скобки. Используя приоритет операций и правила ассоциативности выше, добавьте скобки в каждое выражение так, как если бы их обрабатывал бы компилятор.

Подсказка: Используйте колонку «Пример» в таблице выше, чтобы определить, является ли оператор унарным (имеет один операнд) или бинарным (имеет два операнда).

Например: х = 2 + 3 % 4

Бинарный оператор % имеет более высокий приоритет, чем оператор + или =, поэтому он выполняется первым: х = 2 + (3 % 4). Затем бинарный оператор + имеет более высокий приоритет, чем оператор =, поэтому следующим выполняется именно он.

Ответ: х = (2 + (3 % 4)).

Дальше нам уже не нужна таблица, чтобы понять ход обработки этого выражения компилятором.

Задания:

   x = 3 + 4 + 5

   x = y = z

   z *= ++y + 5

   a || b && c || d

Ответ

x = 3 + 4 + 5

Уровень приоритета бинарного оператора + выше, чем оператора =, поэтому: х = (3 + 4 + 5). Ассоциативность бинарного оператора + слева направо, поэтому ответх = ((3 + 4) + 5).

x = y = z

Ассоциативность бинарного оператора = справа налево, поэтому ответ: x = (y = z).

z *= ++y + 5

Унарный оператор ++ имеет наивысший приоритет, поэтому: z *= (++y) + 5. Затем идёт бинарный оператор +, поэтому ответz *= ((++y) + 5).

a || b && c || d

Бинарный оператор && имеет приоритет выше, чем ||, поэтому: a || (b && c) || d. Ассоциативность бинарного оператора || слева направо, поэтому ответ: (a || (b && c)) || d.


Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (141 оценок, среднее: 4,90 из 5)
Загрузка...

Комментариев: 3

  1. Аватар zashiki:

    здравствуйте, может, где то упоминалось, но не найду: операторы ввода вывода << и >> (к примеру в std::cout<<) — это и есть побитовый сдвиг влево вправо?
    Если нет, то какое место они занимают во всей этой системе операторов?

    1. Аватар Chestor:

      << и >> это всё те же операции битового сдвига влева и вправо соответсвенно, НО, в пространстве имён std эти две операции ПЕРЕОПРЕДЕЛЕНЫ. Думаю, о переопределении, речь пойдёт в след. уроках и Вы узнаете, что это такое. Но можете сейчас погуглить )))

      1. Аватар zashiki:

        Спасибо за ответ! "Гуглить сейчас" не стоило)

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *