Урок №68. Оператор switch

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

 7855

 ǀ   3 

На цьому уроці ми розглянемо ще один оператор управління потоком виконання програми — оператор switch, а також те, навіщо його використовувати і як це робити ефективно.

Навіщо використовувати оператор switch?

Хоча ми можемо використати відразу декілька операторів if/else разом — читається і виглядає це не дуже. Наприклад:

Використання розгалуження if/else для перевірки значення однієї змінної — практика поширена, але мова C++ надає альтернативний і більш ефективний умовний оператор розгалуження switch. Ось вищенаведена програма, але вже з використанням оператора switch:

Загальна ідея операторів switch проста: вираз оператора switch (наприклад, switch(color)) має генерувати значення, а кожен випадок (або “case”) перевіряє це значення на відповідність. Якщо case збігається з виразом switch, то виконуються інструкції під відповідним кейсом. Якщо case не відповідає виразу switch, то виконуються інструкції після кейсу default (якщо його взагалі вказали).

Через свою реалізацію, оператори switch зазвичай більш ефективні, ніж ланцюжки if/else. Давайте розглянемо це більш детально.

Оператор switch


Спочатку пишеться ключове слово switch, за яким слідує вираз, з яким ми хочемо працювати. Зазвичай цим виразом є окрема змінна, але це може бути і щось більш складне, наприклад, nX + 2 чи nX − nY. Єдине обмеження для цього виразу — воно повинно бути інтегрального типу даних (тобто char, short, int, long, long long чи enum). Змінні типу з плаваючою крапкою або неінтегральні типи використовуватися не можуть.

Після виразу switch ми оголошуємо блок. Усередині блоку ми використовуємо лейбли (англ. “labels”) для визначення всіх значень, які ми хочемо перевірити на відповідність з виразом. Існує два типи лейблів.

Лейбли case

Перший вид лейблів — це case (або просто “кейс”), який оголошується з використанням ключового слова case і має константний вираз. Константний вираз — це той вираз, який генерує константне значення, іншими словами це літерал (наприклад, 5), перерахування (наприклад, COLOR_RED) чи константа (наприклад, змінна x, яка була оголошена з ключовим словом const).

Константний вираз, що знаходиться після ключового слова case, перевіряється на відповідність виразу, що знаходяться біля ключового слова switch. Якщо вони збігаються, то тоді виконується код під відповідним кейсом.

Варто відзначити, що всі вирази case повинні генерувати унікальні значення. Тобто ви не зможете зробити наступне:

Можна використовувати відразу декілька кейсів для одного виразу. Наступна функція використовує декілька кейсів для перевірки відповідності параметру p числу з ASCII-таблиці:

У випадку, якщо p є числом з ASCII-таблиці, то виконуватися буде перший стейтмент після кейсу — return true;.

Лейбл default


Другий тип лейбла — це лейбл за замовчуванням (так званий “default case”), який оголошується з використанням ключового слова default. Код під цим лейблом виконується, якщо жоден з кейсів не відповідає виразу switch. Лейбл за замовчуванням є необов’язковим. В одному switch може бути тільки один default. Зазвичай його оголошують останнім в блоці switch.

У вищенаведеному прикладі, якщо p не є числом з ASCII-таблиці, то тоді виконується лейбл за замовчуванням і повертається false.

switch і fall-through

Одна з найбільш каверзних речей в switch — це послідовність виконання коду. Коли відбувся збіг з кейсом (або виконується default), то виконання починається з першого стейтменту, який знаходиться після відповідного кейса і триває до тих пір, поки не буде виконано одну з наступних умов завершення:

   Досягнуто кінець блоку switch.

   Виконується оператор return.

   Виконується оператор goto.

   Виконується оператор break.

Зверніть увагу, якщо жодної з цих умов завершення не буде, то виконуватися будуть всі кейси після того кейса, який співпав з виразом switch. Наприклад:

Результат:

2
3
4
5

А ось це точно не те, що нам потрібно! Коли виконання переходить з одного кейсу в наступний, то це називається fall-through. Програмісти майже ніколи не використовують fall-through, тому в окремих випадках, коли це все-таки використовується — програміст залишає коментар, в якому повідомляє, що fall-through є навмисним.

switch і оператор break


Оператор break (оголошений з використанням ключового слова break) повідомляє компілятору, що ми вже зробили все, що хотіли з певним switch (або циклом while, do while чи for) і більше не маємо наміру з ним працювати. Коли компілятор зустрічає оператор break, то виконання коду переходить зі switch на наступний рядок після блоку switch. Розглянемо вищенаведений приклад, але вже з коректно вставленими операторами break:

Оскільки другий кейс відповідає виразу switch, то виводиться 2, і оператор break завершує виконання блоку switch. Решта кейсів пропускаються.

Попередження: Не забувайте використовувати оператор break в кінці кожного кейсу. Його відсутність — одна з найпоширеніших помилок серед початківців!

Декілька стейтментів всередині блоку switch

Ще одна особливість switch полягає в тому, що ви можете використовувати декілька стейтментів під кожним кейсом, не визначаючи новий блок:

Оголошення змінної і її ініціалізація всередині кейсу

Ви можете оголосити, але не ініціалізувати змінні всередині блоку case:

Зверніть увагу, що хоча змінна z була визначена в першому кейсі, вона також використовується і в другому кейсі. Всі кейси вважаються частиною однієї і тієї ж області видимості, тому, оголосивши змінну в одному кейсі, ми можемо спокійно використовувати її без оголошення і в інших кейсах.

Це може здатися трохи нелогічним, тому давайте розглянемо це детально. Коли ми визначаємо локальну змінну, наприклад, int y;, то змінна не створюється в цій точці — вона фактично створюється на початку блоку, в якому її оголосили. Однак в програмі її не видно до точки оголошення. Саме оголошення не виконується, воно просто повідомляє компілятору, що змінна вже може використовуватися в коді. Тому змінна, оголошена в одному кейсі, може використовуватися в іншому кейсі, навіть якщо кейс, що оголошує змінну, ніколи не виконується.

Проте ініціалізація змінних безпосередньо в кейсах заборонена і викличе помилку компіляції. Це пов’язано з тим, що ініціалізація змінної вимагає виконання, а case, що містить ініціалізацію, — може ніколи не виконатися!

Якщо в кейсі потрібно оголосити і/або ініціалізувати нову змінну, то це найкраще зробити, використовуючи блок стейтментів всередині кейсу:

Правило: Якщо потрібно ініціалізувати і/або оголосити змінні всередині кейсу, то використовуйте блоки стейтментів.

Тест

Завдання №1

Напишіть функцію calculate(), яка приймає дві змінні типу int і одну змінну типу char, яка представляє одну з наступних математичних операцій: +, -, *, / або % (залишок від числа). Використайте switch для виконання відповідної математичної операції над цілими числами і поверніть результат. Якщо в функцію передається недійсний математичний оператор, то функція повинна виводити помилку. З оператором ділення виконайте цілочисельне ділення.

Відповідь №1

Завдання №2

Визначте перерахування (або клас enum) Animal, яке містить наступних тварин: pig, chicken, goat, cat, dog і ostrich. Напишіть функцію getAnimalName(), яка приймає Animal в якості параметра і використовує switch для повернення типу тварини у вигляді рядка. Напишіть ще одну функцію printNumberOfLegs(), яка використовує switch для виводу кількості лапок відповідного типу тварини. Переконайтеся, що обидві функції мають кейс default, який виводить повідомлення про помилку. Викличте printNumberOfLegs() в main(), використовуючи в якості параметрів cat і chicken.

Приклад результату виконання програми:

A cat has 4 legs.
A chicken has 2 legs.

Відповідь №2

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

1 Зірка2 Зірки3 Зірки4 Зірки5 Зірок (61 оцінок, середня: 4,90 з 5)
Завантаження...

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

  1. Артем Вікторович :

    Чи можна у switch використовувати діапазон значень? Якщо діапазон малий, то можна застосувати fall-through, але якщо, наприклад, потрібно діапазон чисел від 10 до 99.

    1. Єгор Авраменко :

      Якщо я правильно зрозумів ваше питання, то ні, неможливо, і краще використовувати if з логічним І:

      1. Артем Вікторович :

        Знайшов статтю, що це можливо і мій компілятор це дозволяє зробити:

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

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