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

  Юрій  | 

  Оновл. 28 Кві 2020  | 

 215

Щоб правильно обчислювати вирази (наприклад, 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
* Розіменування *expression
new Динамічне виділення пам’яті new type
new[] Динамічне виділення масиву new type[expression]
delete Динамічне видалення пам’яті delete pointer
delete[] Динамічне видалення масиву delete[] pointer
4. L -> R ->* Вибір члену через вказівник object_pointer->*pointer_to_member
.* Вибір члену об’єкта 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 вирази, в яких відсутні будь-які дужки. Використовуючи пріоритет операцій і правила асоціативності вище, додайте дужки в кожен вираз так, як би їх оброблював би компілятор.

Підказка: Використовуйте колонку “Приклад” в таблиці вище, щоб визначити чи є оператор унарним (має 1 операнд) чи бінарним (має 2 операнди).

Наприклад: х = 2 + 3 % 4

Бінарний оператор % має більш високий пріоритет, ніж оператор + чи =, тому він виконується першим: х = 2 + (3 % 4). Потім, бінарний оператор + має більш високий пріоритет, ніж оператор =, тому наступним виконується саме він.

Відповідь: х = (2 + (3 % 4)).

Далі нам вже не потрібна таблиця, щоб зрозуміти хід обробки цього виразу компілятором.

Завдання:

   Приклад №1: x = 3 + 4 + 5

   Приклад №2: x = y = z

   Приклад №3: z *= ++y + 5

   Приклад №4: a || b && c || d

Відповідь

Приклад №1: x = 3 + 4 + 5

Рівень пріоритету бінарного оператора + вище, ніж оператора =, тому х = (3 + 4 + 5). Асоціативність бінарного оператора + є зліва направо, тому відповідь: х = ((3 + 4) + 5).

Приклад №2: x = y = z

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

Приклад №3: z *= ++y + 5

Унарний оператор ++ має найвищий пріоритет, тому z *= (++y) + 5. Після нього іде бінарний оператор +, тому відповідьz *= ((++y) + 5).

Приклад №4: a || b && c || d

Бінарний оператор && має вищий пріоритет, ніж оператор ||, тому a || (b && c) || d. Асоціативність бінарного оператора || є зліва направо, тому відповідь: (a || (b && c)) || d.

Оцінити статтю:

1 Зірка2 Зірки3 Зірки4 Зірки5 Зірок (Немає Оцінок)
Loading...

Залишити відповідь

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