Skip to content

➕ Операции в езика C++. Условни оператори. Съставен оператор.

C++ е мощен език за програмиране, който предоставя широк набор от оператори и контролни структури. Овладяването на тези фундаментални концепции е от ключово значение за ефективното програмиране и решаване на алгоритмични задачи в състезателното програмиране.

1. Операции в C++

C++ предоставя богат набор от оператори, които могат да се използват за извършване на изчисления, сравнения, логически операции и манипулация на данни. Разбирането на приоритета и асоциативността на операторите е критично за писането на коректен код.

1.1. Таблица с основни оператори

Тип оператор Пример Описание
Аритметични +, -, *, /, % Използват се за базови математически операции. Оператор % работи само с цели числа.
Логически &&, ||, ! Използват се за логически проверки и условия. && е AND, || е OR, ! е NOT.
Оператори за сравнение ==, !=, <, >, <=, >= Сравняват две стойности и връщат true или false.
Присвояване =, +=, -=, *=, /=, %= Използват се за присвояване на стойности или промяна на променливи.
Инкремент/Декремент ++, -- Увеличават или намаляват стойността на променлива с 1. Има prefix (++i) и postfix (i++) варианти.
Битови операции &, |, ^, ~, <<, >> Работят на ниво битове. Много полезни в състезателното програмиране.
Тернарен оператор условие ? стойност1 : стойност2 Кратък запис на if-else конструкция.

1.2. Подробности за аритметичните оператори

int a = 10, b = 3;
cout << a + b << endl;   // 13 - събиране
cout << a - b << endl;   // 7 - изваждане
cout << a * b << endl;   // 30 - умножение
cout << a / b << endl;   // 3 - целочислено деление (не 3.33!)
cout << a % b << endl;   // 1 - остатък от деление

// ВАЖНО: Деление и остатък с отрицателни числа
int c = -10, d = 3;
cout << c / d << endl;   // -3
cout << c % d << endl;   // -1 (знакът следва делимото в C++)

// За реално деление, използвайте double
double x = 10.0, y = 3.0;
cout << x / y << endl;   // 3.33333...

1.3. Логически оператори и къс-съединение

Къс-съединение (Short-circuit evaluation): Важна оптимизация!

// AND (&&): Ако първото условие е false, второто не се проверява
if (x != 0 && y / x > 5) {  // Безопасно - избягва деление на нула
    // код
}

// OR (||): Ако първото условие е true, второто не се проверява
if (x == 0 || y / x < 5) {  // Безопасно
    // код
}

// NOT (!): Обръща булевата стойност
bool flag = true;
if (!flag) {  // Ще е false
    // код
}

1.4. Битови операции

Битовите операции са изключително мощни и често използвани в състезателното програмиране:

int a = 5;      // 0101 в двоична
int b = 3;      // 0011 в двоична

cout << (a & b) << endl;   // 1 - AND (0001)
cout << (a | b) << endl;   // 7 - OR (0111)
cout << (a ^ b) << endl;   // 6 - XOR (0110)
cout << (~a) << endl;      // -6 - NOT (инвертира всички битове)
cout << (a << 1) << endl;  // 10 - shift наляво (умножение по 2)
cout << (a >> 1) << endl;  // 2 - shift надясно (деление на 2)

// Практически приложения:
// Проверка дали число е четно
if ((n & 1) == 0) { /* четно */ }

// Умножение/деление по степени на 2
int doubled = n << 1;      // n * 2
int halved = n >> 1;       // n / 2

// Проверка на i-тия бит
bool is_set = (n & (1 << i)) != 0;

// Задаване на i-тия бит
n |= (1 << i);

// Изчистване на i-тия бит
n &= ~(1 << i);

// Toggle на i-тия бит
n ^= (1 << i);

1.5. Приоритет на операторите

Важно е да знаете приоритета, за да избегнете неочаквани резултати:

int result = 2 + 3 * 4;     // 14, не 20 (* има по-висок приоритет)
bool check = 5 < 3 || 10 > 8;  // true

// Използвайте скоби за яснота
int result2 = (2 + 3) * 4;  // 20

// Внимавайте с побитовите операции и сравненията
if ((x & mask) == 0) {  // Трябва скоби, защото == има по-висок приоритет от &
    // код
}

2. Условни оператори

Условните оператори контролират потока на изпълнение на програмата въз основа на булеви условия. Те са основата на разклоненията в алгоритмите.

2.1. if условие

Най-простата форма на условна конструкция:

if (условие) {
    // Кодът се изпълнява, ако условието е вярно (true)
}

Пример:

int number = 5;
if (number > 0) {
    cout << "Числото е положително!" << endl;
}

// Можете да проп скоби за един израз (НЕ СЕ ПРЕПОРЪЧВА)
if (number > 0)
    cout << "Положително!" << endl;  // Опасно - лесно се правят грешки

2.2. if-else условие

Позволява алтернативен код, ако условието не е изпълнено:

if (условие) {
    // Кодът се изпълнява, ако условието е вярно
} else {
    // Кодът се изпълнява, ако условието е невярно
}

Пример:

int number = -3;
if (number > 0) {
    cout << "Числото е положително!" << endl;
} else {
    cout << "Числото е отрицателно или нула!" << endl;
}

// Добра практика е винаги да използвате скоби, дори за един ред
if (x > 0) {
    y = 1;
} else {
    y = -1;
}

2.3. if-else if-else верига

За проверка на множество условия последователно:

int score = 85;

if (score >= 90) {
    cout << "Отличен" << endl;
} else if (score >= 80) {
    cout << "Много добър" << endl;
} else if (score >= 70) {
    cout << "Добър" << endl;
} else if (score >= 60) {
    cout << "Среден" << endl;
} else {
    cout << "Слаб" << endl;
}

2.4. Вложени if конструкции

int age = 25;
bool hasLicense = true;

if (age >= 18) {
    if (hasLicense) {
        cout << "Можете да шофирате" << endl;
    } else {
        cout << "Нужен е книжка" << endl;
    }
} else {
    cout << "Твърде млад сте" << endl;
}

2.5. Тернарен оператор (? :)

Кратка форма на if-else за прости присвоявания:

// Синтаксис: условие ? стойност_ако_true : стойност_ако_false
int a = 5, b = 10;
int max = (a > b) ? a : b;  // max = 10

// Еквивалентно на:
int max;
if (a > b) {
    max = a;
} else {
    max = b;
}

// Може да се влагат (но не злоупотребявайте)
int result = (x > 0) ? 1 : (x < 0) ? -1 : 0;  // signum function

2.6. switch-case конструкция

Ефективна при много случаи на една променлива:

switch (променлива) {
    case стойност1:
        // Код за стойност1
        break;  // ВАЖНО: break излиза от switch
    case стойност2:
        // Код за стойност2
        break;
    case стойност3:
    case стойност4:  // Можем да групираме case-ове
        // Код за стойност3 или стойност4
        break;
    default:
        // Код за всички други стойности
        break;  // Опционално за default, но добра практика
}

Примери:

// Пример 1: Дни от седмицата
int day = 3;
switch (day) {
    case 1: cout << "Понеделник"; break;
    case 2: cout << "Вторник"; break;
    case 3: cout << "Сряда"; break;
    case 4: cout << "Четвъртък"; break;
    case 5: cout << "Петък"; break;
    case 6: cout << "Събота"; break;
    case 7: cout << "Неделя"; break;
    default: cout << "Невалиден ден"; break;
}

// Пример 2: Калкулатор
char op = '+';
int a = 10, b = 5;
int result;

switch (op) {
    case '+':
        result = a + b;
        break;
    case '-':
        result = a - b;
        break;
    case '*':
        result = a * b;
        break;
    case '/':
        if (b != 0) result = a / b;
        else cout << "Грешка: деление на нула" << endl;
        break;
    default:
        cout << "Невалидна операция" << endl;
}

// Пример 3: Fall-through (без break - изпълнява следващите case-ове)
int month = 2;
int days;
switch (month) {
    case 1: case 3: case 5: case 7: case 8: case 10: case 12:
        days = 31;
        break;
    case 4: case 6: case 9: case 11:
        days = 30;
        break;
    case 2:
        days = 28;  // Опростено, без високосна година
        break;
    default:
        days = 0;
}

Важни забележки за switch: - Работи само с цели числа и символи (int, char, enum) - НЕ работи с string (използвайте if-else) - Забравеният break води до "fall-through" - често среща грешка - Добра практика е да има default клауза

3. Съставни оператори

Съставните оператори (compound assignment operators) комбинират аритметична операция с присвояване, правейки кода по-компактен и често по-ефективен.

3.1. Таблица със съставни оператори

Оператор Пример Еквивалентен на Описание
+= a += 5; a = a + 5; Прибавя и присвоява
-= a -= 3; a = a - 3; Изважда и присвоява
*= a *= 2; a = a * 2; Умножава и присвоява
/= a /= 4; a = a / 4; Дели и присвоява
%= a %= 2; a = a % 2; Остатък и присвоява
&= a &= b; a = a & b; Битово AND и присвоява
|= a |= b; a = a | b; Битово OR и присвоява
^= a ^= b; a = a ^ b; Битово XOR и присвоява
<<= a <<= 2; a = a << 2; Shift наляво и присвоява
>>= a >>= 2; a = a >> 2; Shift надясно и присвоява

3.2. Примери за използване

int score = 100;
score += 50;     // score станаscore = 150
score -= 20;     // score става 130
score *= 2;      // score става 260
score /= 4;      // score става 65
score %= 10;     // score става 5

// Битови операции
int flags = 0b0001;
flags |= 0b0100;   // Задава битове: flags = 0b0101 (5)
flags &= 0b0111;   // Изчиства битове: flags = 0b0101 (5)
flags ^= 0b0011;   // Toggle битове: flags = 0b0110 (6)

// В цикли - много често използвани
int sum = 0;
for (int i = 1; i <= 10; i++) {
    sum += i;  // По-кратко от sum = sum + i
}

// С масиви
int count[100] = {0};
count[index]++;  // Увеличава стойността на позиция index

3.3. Prefix vs Postfix инкремент/декремент

Важна разлика, която често се пренебрегва:

int a = 5;
int b = ++a;  // Prefix: първо увеличава a, после присвоява на b
              // a = 6, b = 6

int c = 5;
int d = c++;  // Postfix: първо присвоява на d, после увеличава c
              // c = 6, d = 5

// В състезателното програмиране:
// Prefix (++i) е леко по-бърз в циклите
for (int i = 0; i < n; ++i) {  // Предпочитано
    // код
}

// Но разликата е незначителна за int
// За итератори обаче може да има разлика:
for (auto it = v.begin(); it != v.end(); ++it) {  // По-добре
    // код
}

4. Практически съвети и често срещани грешки

4.1. Често срещани грешки

// ГРЕШКА 1: Използване на = вместо == в условия
if (x = 5) {  // ГРЕШКА! Това присвоява 5 на x, не сравнява
    // код
}
if (x == 5) {  // ПРАВИЛНО
    // код
}

// ГРЕШКА 2: Забравен break в switch
switch (x) {
    case 1:
        doSomething();
        // Забравен break - ще падне в case 2!
    case 2:
        doSomethingElse();
        break;
}

// ГРЕШКА 3: Сравняване на float с ==
double x = 0.1 + 0.2;
if (x == 0.3) {  // Може да не сработи заради закръгляне
    // код
}
// По-добре:
const double EPS = 1e-9;
if (abs(x - 0.3) < EPS) {  // ПРАВИЛНО
    // код
}

// ГРЕШКА 4: Деление на цели числа
int a = 5, b = 2;
double result = a / b;  // result = 2.0, не 2.5!
// Правилно:
double result = (double)a / b;  // или double(a) / b

4.2. Добри практики

// Винаги използвайте скоби при if/else, дори за един ред
if (condition) {
    doSomething();
}

// Именувайте булевите променливи ясно
bool isEmpty = (size == 0);
bool hasWon = (score >= 100);

// Използвайте const за стойности, които не се променят
const int MAX_SIZE = 1000;
const double PI = 3.14159265359;

// Избягвайте магически числа - използвайте константи
// ЛОШО:
if (status == 404) { ... }
// ДОБРО:
const int HTTP_NOT_FOUND = 404;
if (status == HTTP_NOT_FOUND) { ... }

4.3. Оптимизации в състезателното програмиране

// Битови операции вместо аритметични (по-бързи)
n *= 2;    n <<= 1;  // Умножение по 2
n /= 2;    n >>= 1;  // Деление на 2
n % 2;     n & 1;    // Проверка за четност

// Swap без temporary променлива (използвайте std::swap обаче)
a ^= b;
b ^= a;
a ^= b;
// Но е по-добре:
swap(a, b);  // Стандартната функция

// Бърза проверка дали число е степен на 2
bool isPowerOf2 = (n > 0) && ((n & (n - 1)) == 0);

🏁 Заключение

C++ предоставя мощни инструменти за работа с данни и контрол на изпълнението на програми. Ключови точки:

  • Разберете приоритета на операторите - използвайте скоби при съмнение
  • Внимавайте с типовете данни - особено при деление и сравнения на float
  • Използвайте подходящата конструкция - if-else за малко случаи, switch за много
  • Битовите операции са мощни - научете ги добре за състезателно програмиране
  • Винаги ползвайте скоби при if/else - предотвратява много грешки

Съчетаването на оператори и условни конструкции позволява ефективно решаване на различни алгоритмични задачи. Практикувайте редовно, за да развиете интуиция кога коя конструкция е най-подходяща! 🚀