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

  Юрій  | 

  Оновл. 4 Вер 2021  | 

 716

Щоб правильно обчислювати вирази (наприклад, 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. Ні :: Глобальна область видимості (унарний) ::ідентифікатор
:: Область видимості класу (бінарний) назва_класу::назва_члену
2. L -> R () Круглі дужки (вираз)
() Виклик функції назва_функції(параметри)
() Ініціалізація назва_типу(вираз)
{} uniform-ініціалізація (C++11) назва_типу{вираз}
type() Конвертація типу новий_тип(вираз)
type{} Конвертація типу (C++11) новий_тип{вираз}
[] Індекс масиву вказівник[вираз]
. Доступ до члену об’єкта об’єкт.назва_члену
-> Доступ до члену об’єкта через вказівник вказівник_об’єкту->назва_члену
++ Пост-інкремент lvalue++
–– Пост-декремент lvalue––
typeid Інформація про тип під час виконання typeid(тип) або typeid(вираз)
const_cast Cast away const const_cast(вираз)
dynamic_cast Type-checked cast під час виконання dynamic_cast(вираз)
reinterpret_cast Конвертація одного типу в інший reinterpret_cast(вираз)
static_cast Type-checked cast під час компіляції static_cast(вираз)
3. R -> L + Унарний плюс +вираз
Унарний мінус -вираз
++ Пре-інкремент ++lvalue
–– Пре-декремент ––lvalue
! Логічне НЕ (NOT) !вираз
~ Побітове НЕ (NOT) ~вираз
(type) C-style cast (новий_тип)вираз
sizeof Розмір в байтах sizeof(тип) або sizeof(вираз)
& Адреса &lvalue
* Розіменування *вираз
new Динамічне виділення пам’яті new тип
new[] Динамічне виділення масиву new тип[вираз]
delete Динамічне видалення пам’яті delete вказівник
delete[] Динамічне видалення масиву delete[] вказівник
4. L -> R ->* Вибір члену через вказівник вказівник_об’єкту->*вказівник_на_член
.* Вибір члену об’єкта об’єкт.*вказівник_на_член
5. L -> R * Множення вираз * вираз
/ Ділення вираз / вираз
% Залишок вираз % вираз
6. L -> R + Додавання вираз + вираз
Віднімання вираз – вираз
7. L -> R << Побітовий зсув вліво вираз << вираз
>> Побітовий зсув вправо вираз >> вираз
8. L -> R < Порівняння: менше вираз < вираз
<= Порівняння: менше/дорівнює вираз <= вираз
> Порівняння: більше вираз > вираз
>= Порівняння: більше/дорівнює вираз >= вираз
9. L -> R == Дорівнює вираз == вираз
!= Не дорівнює вираз != вираз
10. L -> R & Побітове І (AND) вираз & вираз
11. L -> R ^ Побітове виключне АБО (XOR) вираз ^ вираз
12. L -> R | Побітове АБО (OR) вираз | вираз
13. L -> R && Логічне І (AND) вираз && вираз
14. L -> R || Логічне АБО (OR) вираз || вираз
15. R -> L ?: Тернарний умовний оператор вираз ? вираз : вираз
= Присвоювання lvalue = вираз
*= Множення з присвоюванням lvalue *= вираз
/= Ділення з присвоюванням lvalue /= вираз
%= Ділення з остачею і з присвоюванням lvalue %= вираз
+= Додавання з присвоюванням lvalue += вираз
-= Віднімання з присвоюванням lvalue -= вираз
<<= Присвоювання з побітовим зсувом вліво lvalue <<= вираз
>>= Присвоювання з побітовим зсувом вправо lvalue >>= вираз
&= Присвоювання з побітовою операцією І (AND) lvalue &= вираз
|= Присвоювання з побітовою операцією АБО (OR) lvalue |= вираз
^= Присвоювання з побітовою операцією “Виключне АБО” (XOR) lvalue ^= вираз
16. R -> L throw Генерація винятку throw вираз
17. L -> R , Оператор Кома вираз, вираз

Деякі оператори ви вже знаєте з попередніх уроків: +, -, *, /, (), =, < і >. Їх значення однакові як в математиці, так і в мові 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 Зірок (6 оцінок, середня: 5,00 з 5)
Loading...

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

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