➕ Операции в езика 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 условие
Най-простата форма на условна конструкция:
Пример:
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 - предотвратява много грешки
Съчетаването на оператори и условни конструкции позволява ефективно решаване на различни алгоритмични задачи. Практикувайте редовно, за да развиете интуиция кога коя конструкция е най-подходяща! 🚀