Оператори, перетворення типів та логічні операції
Мова операторів: від виразів до рішень
До цього моменту ви вже вмієте зберігати дані у змінних і вводити їх з клавіатури. Але програма — це не просто сховище даних. Програма думає: вона порівнює числа, приймає рішення, виконує дії залежно від умов. Інструментом для всього цього є оператори.
Оператор (operator) — це символ або ключове слово, яке дає компілятору команду виконати певну дію над одним або кількома операндами (operands). Операнди — це дані, з якими виконується операція: значення, змінні або вирази.
int result = 10 + 3;
// ↑ ↑
// операнди: 10 і 3
// ↑
// оператор: +
Усі оператори C++ поділяються на кілька груп за призначенням:
➕ Арифметичні
+, -, *, /, %✏️ Присвоєння
=, +=, -=, *=, /=, %=⚖️ Порівняння
==, !=, <, >, <=, >=🔗 Логічні
&&, ||, !↕️ Інкремент / Декремент
++, --🔢 Побітові
&, |, ^, ~, <<, >>У цій статті ми детально розберемо перші п'ять груп — саме їх ви будете використовувати щодня.
Арифметичні оператори
Ви вже зустрічалися з ними у попередньому розділі. Розглянемо їх систематично та зверніть увагу на деталі, які часто є джерелом помилок.
| Оператор | Назва | Приклад | Результат |
|---|---|---|---|
+ | Додавання | 7 + 3 | 10 |
- | Віднімання | 7 - 3 | 4 |
* | Множення | 7 * 3 | 21 |
/ | Ділення | 7 / 3 | 2 (ціле!) |
% | Остача від ділення | 7 % 3 | 1 |
Ціле ділення та його пастка
Найбільш несподіваний оператор для новачків — це /. У C++ поведінка оператора ділення залежить від типів операндів.
Якщо обидва операнди є цілими числами (int, short, long тощо), C++ виконує ціле ділення (integer division): дробова частина результату просто відкидається, а не округлюється.
int a = 7;
int b = 3;
int result = a / b; // result = 2, а не 2.333...
Зверніть увагу на принципову відмінність: 7 / 3 = 2, а не 2.33. Дробова частина .33 не округлюється до нуля — вона знищується. Якщо вам потрібен дійсний результат, хоча б один із операндів повинен бути типу float або double:
int a = 7;
int b = 3;
double result1 = a / b; // result1 = 2.0 ← НЕПРАВИЛЬНО!
double result2 = (double)a / b; // result2 = 2.333... ← Правильно
double result3 = a / 3.0; // result3 = 2.333... ← Теж правильно
У першому рядку ціле ділення відбувається до присвоєння в double: спочатку 7 / 3 = 2, а потім 2 конвертується у 2.0. Результат некоректний. У другому та третьому рядках хоча б один з операндів є дійсним числом — тому і ділення стає дійсним.
0, 1 або інше округлене значення замість дробового — перевірте типи операндів у виразі ділення.Оператор остачі %
Оператор % (модуль або остача від ділення) повертає залишок від цілого ділення. Він застосовується лише до цілих типів.
cout << 10 % 3; // 1 (10 = 3*3 + 1)
cout << 14 % 7; // 0 (14 = 7*2 + 0)
cout << 5 % 8; // 5 (5 = 8*0 + 5)
Навіщо він потрібен? Декілька практичних застосувань:
- Перевірка парності: якщо
n % 2 == 0, число парне. - Циклічне лічення: наприклад, дні тижня від
0до6. Якщо деньd + 3може вийти за межі, використайте(d + 3) % 7. - Виділення цифр: остання цифра числа
n— цеn % 10, передостання —(n / 10) % 10.
Пріоритет арифметичних операцій
C++ дотримується математичного пріоритету (precedence): множення, ділення та остача виконуються до додавання та віднімання. Для зміни порядку використовуйте дужки.
int a = 2 + 3 * 4; // a = 14 (спочатку 3*4=12, потім 2+12)
int b = (2 + 3) * 4; // b = 20 (дужки змінюють порядок)
int c = 10 - 6 / 2; // c = 7 (спочатку 6/2=3, потім 10-3)
Оператори присвоєння
Звичайний оператор присвоєння = ви вже знаєте. Але C++ має скорочені форми, які суміщають арифметичну операцію з присвоєнням. Вони називаються складеними операторами присвоєння (compound assignment operators).
Складені оператори присвоєння
| Оператор | Еквівалент | Приклад | Результат |
|---|---|---|---|
x += n | x = x + n | x += 5 | Збільшити x на 5 |
x -= n | x = x - n | x -= 3 | Зменшити x на 3 |
x *= n | x = x * n | x *= 2 | Подвоїти x |
x /= n | x = x / n | x /= 4 | Поділити x на 4 |
x %= n | x = x % n | x %= 7 | Залишити лише остачу від ділення x на 7 |
int score = 100;
score += 50; // score = 150 (нараховуємо бонуси)
score -= 30; // score = 120 (штраф)
score *= 2; // score = 240 (подвоєння)
score /= 3; // score = 80 (ціле ділення!)
Ці оператори не просто «зручне скорочення» — вони роблять код читабельнішим, явно показуючи, що ми модифікуємо існуючу змінну, а не присвоюємо їй нове незалежне значення.
Інкремент та декремент
Окремий та дуже поширений випадок — збільшення або зменшення значення рівно на одиницю. Для цього C++ має спеціальні оператори.
Інкремент (increment) ++ — збільшує значення змінної на 1.
Декремент (decrement) -- — зменшує значення змінної на 1.
int counter = 0;
counter++; // counter = 1
counter++; // counter = 2
counter--; // counter = 1
Кожен з цих операторів існує у двох формах: префіксній (перед змінною) та постфіксній (після змінної). Різниця проявляється лише тоді, коли вираз використовується у ширшому контексті — наприклад, при виводі або присвоєнні.
Спочатку збільшує значення, потім повертає вже збільшене значення.
int x = 5;
int y = ++x; // Спочатку x стає 6, потім y = 6
// x = 6, y = 6
Аналогія: «Спочатку дій, потім звітуй».
Спочатку повертає поточне значення, а потім збільшує його.
int x = 5;
int y = x++; // y = 5 (береться поточне значення), потім x стає 6
// x = 6, y = 5
Аналогія: «Спочатку звітуй, потім дій».
counter++;), між префіксною та постфіксною формами немає жодної різниці — обидва варіанти просто збільшують значення на 1. Різниця виникає лише всередині складніших виразів.Перетворення типів
У C++ типи суворо визначені. Проте іноді нам потрібно «переключитися» між типами: взяти ціле число і зробити з нього дійсне, або навпаки. Для цього існують механізми перетворення типів (type conversion).
Неявне перетворення (implicit conversion)
Коли значення одного типу автоматично перетворюється в інший без жодних додаткових команд — це неявне перетворення. Компілятор виконує його самостійно, коли типи операндів не збігаються.
Загальне правило: при змішуванні різних числових типів C++ розширює менший тип до більшого (щоб уникнути втрати інформації).
int age = 25;
double ratio = age; // int → double: неявне розширення (безпечно)
// ratio = 25.0
double pi = 3.14159;
int piInt = pi; // double → int: неявне звуження (небезпечно!)
// piInt = 3 (дробова частина ЗАГУБЛЕНА)
Ієрархія неявного розширення виглядає таким чином:
Кожен тип автоматично й безпечно розширюється до наступного. Рух у зворотному напрямку (звуження) — небезпечний, адже веде до втрати даних.
double до int не дає похибки компіляції, але мовчки знищує дробову частину. Компілятор може видати warning (попередження), але не зупинить компіляцію. Це один з найнебезпечніших аспектів неявних перетворень.Явне перетворення (explicit cast)
Коли перетворення потрібно виконати свідомо та навмисно, використовується явне приведення типу (type cast). Синтаксис у стилі C, який прийнятий у цьому курсі:
(тип) вираз
int a = 7;
int b = 3;
double result = (double)a / b; // 7.0 / 3 = 2.333...
Тут (double)a — це явне перетворення значення змінної a до типу double. Сама змінна a при цьому не змінюється — вона залишається int. Перетворюється лише значення, яке використовується у цьому виразі.
Розглянемо ще один типовий приклад — виведення відсотка:
int correct = 7;
int total = 10;
// Неправильно: 7 / 10 = 0 (ціле ділення), 0 * 100 = 0
double percent1 = correct / total * 100;
// Правильно: (7.0) / 10 = 0.7, 0.7 * 100 = 70.0
double percent2 = (double)correct / total * 100;
cout << "Percent: " << percent2 << "%\n"; // Percent: 70%
(тип), коли:- ділите два цілих числа і потребуєте дійсного результату
- конвертуєте між
intіchar(код символу) - хочете явно показати читачу, що перетворення свідоме, а не випадкове
Перетворення char та int
Тип char зберігає один символ, але внутрішньо він є числом — кодом символу за таблицею ASCII. Це дає змогу конвертувати між символами та їх числовими кодами.
char letter = 'A';
int code = (int)letter; // code = 65 (код символу 'A' в ASCII)
int number = 66;
char symbol = (char)number; // symbol = 'B' (символ з кодом 66)
cout << "Код 'A': " << code << "\n"; // Код 'A': 65
cout << "Символ 66: " << symbol << "\n"; // Символ 66: B
Ця властивість використовується, наприклад, для визначення, чи є символ літерою, цифрою, або для переведення між регістрами:
char lower = 'a';
char upper = (char)(lower - 32); // 'a'(97) - 32 = 'A'(65)
cout << upper; // A
Оператори порівняння
Щоб програма могла приймати рішення, їй потрібно вміти порівнювати значення. Для цього використовуються оператори порівняння (relational / comparison operators). На відміну від арифметичних операторів, вони повертають не число, а логічне значення типу bool: true (істина) або false (хибність).
| Оператор | Значення | Приклад | Результат |
|---|---|---|---|
== | Дорівнює | 5 == 5 | true |
!= | Не дорівнює | 5 != 3 | true |
< | Менше | 3 < 5 | true |
> | Більше | 3 > 5 | false |
<= | Менше або дорівнює | 5 <= 5 | true |
>= | Більше або дорівнює | 6 >= 5 | true |
== (два знаки рівності). Оператор присвоєння — = (один знак). Написавши if (x = 5) замість if (x == 5), ви не порівняєте x з 5 — ви присвоїтеx значення 5, і умова завжди буде true (непорожнє значення = true). Компілятор часто not попереджає про це.Де застосовуються оператори порівняння?
Результат порівняння — значення bool. Його можна зберегти у змінній:
int age = 20;
bool isAdult = (age >= 18); // true
bool isChild = (age < 12); // false
cout << isAdult; // Виведе: 1 (true відображається як 1)
cout << isChild; // Виведе: 0 (false відображається як 0)
Або використати напряму в умовній конструкції (детальніше про if — у наступному розділі):
if (age >= 18) {
cout << "Доступ дозволено\n";
}
Логічні оператори
Поодинці оператори порівняння дозволяють перевірити лише одну умову: «вік більший за 18» або «пароль вірний». Але реальні програми часто потребують складніших перевірок: «вік більший за 18 І є квиток» або «температура вище 37 АБО є симптоми». Для об'єднання кількох умов в одну використовуються логічні оператори (logical operators).
Оператор І — && (логічне AND)
Повертає true лише якщо обидві умови є істинними одночасно. Достатньо хоч одній умові бути false — і весь вираз стає false.
Таблиця істинності &&:
| Умова A | Умова B | A && B |
|---|---|---|
false | false | false |
false | true | false |
true | false | false |
true | true | true |
int age = 20;
bool hasTicket = true;
bool canEnter = (age >= 18) && hasTicket;
// true && true → true: вхід дозволено
Аналогія з життя: ви заходите у клуб лише якщо І ви повнолітній, І у вас є квиток.
Оператор АБО — || (логічне OR)
Повертає true якщо хоча б одна з умов є істинною. Повертає false лише якщо обидві умови хибні.
Таблиця істинності ||:
| Умова A | Умова B | A || B |
|---|---|---|
false | false | false |
false | true | true |
true | false | true |
true | true | true |
bool hasPass = false;
bool isEmployee = true;
bool canGoIn = hasPass || isEmployee;
// false || true → true: вхід дозволено
Аналогія: Ви можете увійти у службовий вхід, якщо АБО маєте перепустку, АБО є співробітником (є хоча б одна підстава).
Оператор НЕ — ! (логічне NOT)
Інвертує логічне значення: перетворює true на false, і навпаки. Це унарний оператор — він діє на один операнд.
| Умова A | !A |
|---|---|
false | true |
true | false |
bool isRaining = false;
bool goForAWalk = !isRaining; // true: не дощить → можна гуляти
bool isDividableBy2 = (number % 2 == 0);
bool isOdd = !isDividableBy2; // інвертуємо: ділиться → підраховуємо непарне
Складні логічні вирази
Логічні оператори можна комбінувати для побудови складних умов. Пріоритет операцій: ! виконується першим, потім &&, потім ||. Дужки дозволяють перевизначити порядок.
int temperature = 38;
bool hasCough = true;
bool hasVaccine = false;
// Пацієнт у групі ризику: висока температура АБО (кашель І без щеплення)
bool isAtRisk = (temperature > 37) || (hasCough && !hasVaccine);
// true || (true && true) → true || true → true
Розберемо, як обчислюється цей вираз покроково:
!hasVaccine→!false→truehasCough && true→true && true→true(temperature > 37)→(38 > 37)→truetrue || true→true
&&: якщо перша умова — false, результат завжди false, друга умова не перевіряється. Для ||: якщо перша умова — true, результат завжди true. Це важливо, коли друга умова містить виклик функції або звернення до пам'яті.Пріоритет усіх операторів
У складних виразах порядок виконання операторів визначається їх пріоритетом (precedence). Ось спрощена таблиця від найвищого до найнижчого:
| Пріоритет | Оператори | Напрям виконання |
|---|---|---|
| 1 (найвищий) | () дужки | зліва направо |
| 2 | !, ++, -- (префіксні), (тип) | справа наліво |
| 3 | *, /, % | зліва направо |
| 4 | +, - | зліва направо |
| 5 | <, >, <=, >= | зліва направо |
| 6 | ==, != | зліва направо |
| 7 | && | зліва направо |
| 8 | || | зліва направо |
| 9 (найнижчий) | =, +=, -=, *=, /=, %= | справа наліво |
(a > 0) && (b != 0) || !flag значно читабельніший, ніж a > 0 && b != 0 || !flag, навіть якщо результат однаковий.Практичний приклад: Перевірка трикутника
Складемо програму, яка зчитує три сторони та перевіряє, чи можуть вони утворити трикутник. За теоремою: трикутник існує, якщо кожна зі сторін менша за суму двох інших.
#include <iostream>
using namespace std;
int main()
{
double a, b, c;
cout << "Enter three sides: ";
cin >> a >> b >> c;
bool isValid = (a + b > c) && (a + c > b) && (b + c > a);
cout << "Is triangle: " << isValid << "\n";
// Виведе 1 (true) або 0 (false)
return 0;
}
Розберемо ключові елементи:
- Рядок 10: Зчитуємо три числа через пробіл —
cinавтоматично розбиває введення за пробілами. - Рядок 12: Перевірка складається з трьох умов, з'єднаних
&&. Усі три повинні бутиtrueодночасно, щоб трикутник існував. - Рядок 14:
boolпри виводі черезcoutвідображається як0або1.
Приклади роботи:
Enter three sides: 3 4 5
Is triangle: 1
Enter three sides: 1 2 10
Is triangle: 0
Практичний приклад: Конвертер одиниць з відсотком
Розробимо програму, що переводить температуру із Цельсія у Фаренгейт і відразу показує відсоток від «точки кипіння» (100°C = 212°F).
#include <iostream>
using namespace std;
int main()
{
double celsius;
cout << "Enter temperature in Celsius: ";
cin >> celsius;
// Формула перетворення
double fahrenheit = celsius * 9.0 / 5.0 + 32.0;
// Відсоток від точки кипіння (100 C)
double percentOfBoiling = celsius / 100.0 * 100.0;
// Чи перевищує температура поріг нормального тіла (36.6)?
bool isHighTemp = celsius > 36.6;
cout << celsius << " C = " << fahrenheit << " F\n";
cout << "Percent of boiling: " << percentOfBoiling << "%\n";
cout << "High temperature: " << isHighTemp << "\n";
return 0;
}
Аналіз ключових рядків:
- Рядок 13: Формула
* 9.0 / 5.0використовує дійсні літерали (9.0,5.0), щоб ділення не було цілочисельним. Якщо написати* 9 / 5, приcelsius = 37отримуємо37 * 9 / 5 = 333 / 5 = 66(ціле ділення!), а не правильне66.6. - Рядок 16: Тут теж поділ на
100.0(дійсне), а не100. - Рядок 19: Результат порівняння зберігається у
bool isHighTempі виводиться як0або1.
Приклад роботи:
Enter temperature in Celsius: 37
37 C = 98.6 F
Percent of boiling: 37%
High temperature: 1
Практичні завдання
Рівень 1 — Базовий
Визначте значення виразів, не запускаючи програму. Потім перевірте свої відповіді кодом:
int a = 10, b = 3;
cout << a / b << "\n"; // ?
cout << a % b << "\n"; // ?
cout << a / 3.0 << "\n"; // ?
cout << (a > b) << "\n"; // ?
cout << (a == 10) << "\n"; // ?
cout << (a != b) << "\n"; // ?
cout << !true << "\n"; // ?
cout << (a > 5 && b < 5) << "\n"; // ?
Знайдіть та виправте всі помилки у фрагменті коду:
int x = 7, y = 2;
double result = x / y; // Має бути 3.5
bool isEqual = (x = y); // Має перевіряти рівність
bool isPositive = x > 0 && < 100; // Синтаксична помилка
Скільки помилок ви знайшли?
Рівень 2 — Логічний
Напишіть програму, яка зчитує ціле число та виводить 1, якщо воно парне, і 0, якщо непарне. Використовуйте оператор % та збережіть результат у bool.
Перевірка:
- Введено
14→1(парне) - Введено
7→0(непарне)
Напишіть програму, яка зчитує тризначне число (наприклад, 357) та виводить окремо кожну цифру:
- Перша цифра (сотні):
3 - Друга цифра (десятки):
5 - Третя цифра (одиниці):
7
Підказка: цифра одиниць — n % 10, цифра десятків — (n / 10) % 10, цифра сотень — n / 100.
Напишіть програму, яка зчитує рік та виводить 1, якщо він високосний, і 0 — якщо ні.
Рік є високосним, якщо:
- Ділиться на 4 І не ділиться на 100
- АБО ділиться на 400
Збережіть результат у bool leapYear та виведіть його.
Перевірка: 2000 → 1, 1900 → 0, 2024 → 1, 2023 → 0
Рівень 3 — Творчий
Реалізуйте програму-калькулятор індексу маси тіла (BMI). Програма повинна:
- Зчитати вагу (кг) та зріст (м)
- Обчислити BMI за формулою:
BMI = weight / (height * height) - Вивести значення BMI
- Вивести
1/0для двох перевірок:isNormal: чи знаходиться BMI у нормальному діапазоні (від 18.5 до 24.9 включно)isOverweight: чи BMI більший або рівний 25.0
Приклад:
Enter weight (kg): 75
Enter height (m): 1.75
BMI: 24.49
Normal weight: 1
Overweight: 0
Напишіть програму, яка зчитує цифру від 0 до 9 (як char), конвертує її в числове значення (int) та виводить її квадрат.
Підказка: якщо char digit = '7', то числове значення — (int)digit - (int)'0' (різниця між кодом символу та кодом символу '0').
Приклад:
Enter digit: 7
Digit value: 7
Square: 49
Підсумок
📌 Арифметичні оператори
+, -, *, /, %. Ціле ділення відкидає дробову частину — для дійсного результату хоча б один операнд має бути double/float.📌 Оператори присвоєння
=, +=, -=, *=, /=, %= — суміщують операцію з присвоєнням. ++/-- — інкремент та декремент.📌 Перетворення типів
(тип)вираз — свідоме перетворення. Звуження типу (double → int) знищує дробову частину.📌 Оператори порівняння
==, !=, <, >, <=, >= — повертають bool. Пам'ятайте: == (порівняння) ≠ = (присвоєння)!📌 Логічні оператори
&& (AND) — обидві умови, || (OR) — хоча б одна, ! (NOT) — інверсія. Підтримують короткочасне обчислення.📌 Пріоритет
! → */% → +- → <><=>= → ==!= → && → || → =. Сумнівно — ставте дужки.Ввід даних з клавіатури
Оператор cin для зчитування даних з клавіатури. Інтерактивні програми з введенням та виведенням, арифметичні оператори та практичні приклади.
Цикли
Цикли for, while та do-while у C++. Оператори break та continue. Вкладені цикли. Алгоритми з накопиченням, пошуком та лічильниками. Практичні задачі.