C++

Оператори, перетворення типів та логічні операції

Арифметичні, присвоєння, порівняння та логічні оператори C++. Пріоритет операцій. Явне та неявне перетворення типів. Операторні скорочення. Побудова складних умовних виразів.

Мова операторів: від виразів до рішень

До цього моменту ви вже вмієте зберігати дані у змінних і вводити їх з клавіатури. Але програма — це не просто сховище даних. Програма думає: вона порівнює числа, приймає рішення, виконує дії залежно від умов. Інструментом для всього цього є оператори.

Оператор (operator) — це символ або ключове слово, яке дає компілятору команду виконати певну дію над одним або кількома операндами (operands). Операнди — це дані, з якими виконується операція: значення, змінні або вирази.

int result = 10 + 3;
//            ↑   ↑
//       операнди: 10 і 3
//              ↑
//       оператор: +

Усі оператори C++ поділяються на кілька груп за призначенням:

➕ Арифметичні

Виконують математичні обчислення: +, -, *, /, %

✏️ Присвоєння

Записують значення у змінну: =, +=, -=, *=, /=, %=

⚖️ Порівняння

Порівнюють два значення: ==, !=, <, >, <=, >=

🔗 Логічні

Об'єднують умови: &&, ||, !

↕️ Інкремент / Декремент

Збільшують або зменшують значення на 1: ++, --

🔢 Побітові

Маніпулюють окремими бітами: &, |, ^, ~, <<, >>

У цій статті ми детально розберемо перші п'ять груп — саме їх ви будете використовувати щодня.

Арифметичні оператори

Ви вже зустрічалися з ними у попередньому розділі. Розглянемо їх систематично та зверніть увагу на деталі, які часто є джерелом помилок.

ОператорНазваПрикладРезультат
+Додавання7 + 310
-Віднімання7 - 34
*Множення7 * 321
/Ділення7 / 32 (ціле!)
%Остача від ділення7 % 31

Ціле ділення та його пастка

Найбільш несподіваний оператор для новачків — це /. У 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 += nx = x + nx += 5Збільшити x на 5
x -= nx = x - nx -= 3Зменшити x на 3
x *= nx = x * nx *= 2Подвоїти x
x /= nx = x / nx /= 4Поділити x на 4
x %= nx = x % nx %= 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

Аналогія: «Спочатку дій, потім звітуй».

Якщо інкремент стоїть у окремому рядку (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 (дробова частина ЗАГУБЛЕНА)

Ієрархія неявного розширення виглядає таким чином:

Loading diagram...
graph LR
    A["bool"] --> B["char"]
    B --> C["short"]
    C --> D["int"]
    D --> E["long"]
    E --> F["long long"]
    F --> G["float"]
    G --> H["double"]

    style A fill:#64748b,stroke:#334155,color:#ffffff
    style H fill:#3b82f6,stroke:#1d4ed8,color:#ffffff
    style D fill:#f59e0b,stroke:#b45309,color:#ffffff

Кожен тип автоматично й безпечно розширюється до наступного. Рух у зворотному напрямку (звуження) — небезпечний, адже веде до втрати даних.

Перетворення від 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
Таблиця ASCII визначає коди для 128 базових символів. Великі латинські літери: A=65, B=66 ... Z=90. Малі: a=97, b=98 ... z=122. Різниця між відповідними регістрами завжди становить 32.

Оператори порівняння

Щоб програма могла приймати рішення, їй потрібно вміти порівнювати значення. Для цього використовуються оператори порівняння (relational / comparison operators). На відміну від арифметичних операторів, вони повертають не число, а логічне значення типу bool: true (істина) або false (хибність).

ОператорЗначенняПрикладРезультат
==Дорівнює5 == 5true
!=Не дорівнює5 != 3true
<Менше3 < 5true
>Більше3 > 5false
<=Менше або дорівнює5 <= 5true
>=Більше або дорівнює6 >= 5true
Критична помилка-пастка! Оператор порівняння — == (два знаки рівності). Оператор присвоєння — = (один знак). Написавши 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Умова BA && B
falsefalsefalse
falsetruefalse
truefalsefalse
truetruetrue
int age = 20;
bool hasTicket = true;

bool canEnter = (age >= 18) && hasTicket;
// true && true → true: вхід дозволено

Аналогія з життя: ви заходите у клуб лише якщо І ви повнолітній, І у вас є квиток.

Оператор АБО — || (логічне OR)

Повертає true якщо хоча б одна з умов є істинною. Повертає false лише якщо обидві умови хибні.

Таблиця істинності ||:

Умова AУмова BA || B
falsefalsefalse
falsetruetrue
truefalsetrue
truetruetrue
bool hasPass = false;
bool isEmployee = true;

bool canGoIn = hasPass || isEmployee;
// false || true → true: вхід дозволено

Аналогія: Ви можете увійти у службовий вхід, якщо АБО маєте перепустку, АБО є співробітником (є хоча б одна підстава).

Оператор НЕ — ! (логічне NOT)

Інвертує логічне значення: перетворює true на false, і навпаки. Це унарний оператор — він діє на один операнд.

Умова A!A
falsetrue
truefalse
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

Розберемо, як обчислюється цей вираз покроково:

  1. !hasVaccine!falsetrue
  2. hasCough && truetrue && truetrue
  3. (temperature > 37)(38 > 37)true
  4. true || truetrue
Короткочасне обчислення (short-circuit evaluation): C++ не обчислює другу частину логічного виразу, якщо результат вже визначений після першої. Для &&: якщо перша умова — false, результат завжди false, друга умова не перевіряється. Для ||: якщо перша умова — true, результат завжди true. Це важливо, коли друга умова містить виклик функції або звернення до пам'яті.

Пріоритет усіх операторів

У складних виразах порядок виконання операторів визначається їх пріоритетом (precedence). Ось спрощена таблиця від найвищого до найнижчого:

ПріоритетОператориНапрям виконання
1 (найвищий)() дужкизліва направо
2!, ++, -- (префіксні), (тип)справа наліво
3*, /, %зліва направо
4+, -зліва направо
5<, >, <=, >=зліва направо
6==, !=зліва направо
7&&зліва направо
8||зліва направо
9 (найнижчий)=, +=, -=, *=, /=, %=справа наліво
Loading diagram...
graph TD
    A["Вираз: a > 0 && b != 0 || !flag"] --> B["Крок 1: ! (NOT)"]
    B --> C["!flag → true/false"]
    C --> D["Крок 2: > і != (порівняння)"]
    D --> E["a > 0 → bool, b != 0 → bool"]
    E --> F["Крок 3: && (AND)"]
    F --> G["(a > 0) && (b != 0) → bool"]
    G --> H["Крок 4: || (OR)"]
    H --> I["... || !flag → фінальний bool"]

    style A fill:#3b82f6,stroke:#1d4ed8,color:#ffffff
    style I fill:#3b82f6,stroke:#1d4ed8,color:#ffffff
    style F fill:#f59e0b,stroke:#b45309,color:#ffffff
    style H fill:#64748b,stroke:#334155,color:#ffffff
Практична порада: не покладайтеся на пам'ять. Щоразу, коли виникають сумніви щодо порядку виконання, ставте дужки. Код (a > 0) && (b != 0) || !flag значно читабельніший, ніж a > 0 && b != 0 || !flag, навіть якщо результат однаковий.

Практичний приклад: Перевірка трикутника

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

Triangle.cpp
#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).

TempConverter.cpp
#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 — Базовий

Рівень 2 — Логічний

Рівень 3 — Творчий

Підсумок

📌 Арифметичні оператори

+, -, *, /, %. Ціле ділення відкидає дробову частину — для дійсного результату хоча б один операнд має бути double/float.

📌 Оператори присвоєння

=, +=, -=, *=, /=, %= — суміщують операцію з присвоєнням. ++/-- — інкремент та декремент.

📌 Перетворення типів

Неявне (автоматичне) — компілятор розширює тип. Явне (тип)вираз — свідоме перетворення. Звуження типу (double → int) знищує дробову частину.

📌 Оператори порівняння

==, !=, <, >, <=, >= — повертають bool. Пам'ятайте: == (порівняння) ≠ = (присвоєння)!

📌 Логічні оператори

&& (AND) — обидві умови, || (OR) — хоча б одна, ! (NOT) — інверсія. Підтримують короткочасне обчислення.

📌 Пріоритет

!*/%+-<><=>===!=&&||=. Сумнівно — ставте дужки.
Copyright © 2026