Оператори в C++

Операторите в езика C++ участват в изрази. Те пресмятат стойности на променливи. Или върната стойност от функция, участваща в израза. Самите оператори са част от израза. Израза е математическа форумула подходяща в контекста на функция. Която е част от програмата ви.

Операторите се подразделят на някоколко вида според предназначението си:

  1. Аритментични оператори

Това са обикновените оператори от математиката, които познаваме:

  • + Събиране;
  • – Изваждане;
  • * Умножение;
  • / Деление;
  • % Остатък след целочислено деление;

Проверка за честност с оператора %

#include <iostream>
using namespace std;

int main() {
    int num = 8;

    if (num % 2 == 0)
        cout << "Even" << endl;
    else
        cout << "Odd" << endl;
}

2. Оператори за сравнение

Тези оператори връщат bool.

  • == Равно;
  • != Различно (не е равно);
  • > По голямо;
  • < По малко;
  • >= По голямо или равно;
  • <= По малко или равно;

3. Логически оператори

Вече ги разгледахме в предишния урок. Връщат bool. Участват в сложни булеви конструкции.

  • &&, and – връща true, само когато двата операнда са true;
  • ||, or – връща true, ако един операнд е true;
  • !, not – обръща резултата;

4. Оператори за присвояване

Кратката форма е:

=    // присвояване
+=   // a += b →   a = a + b
-=   // a -= b →   a = a - b
*=   // a *= b →   a = a * b
/=   // a /= b →   a = a / b
%=   // a %= b →   a = a % b

5. Инкремент и декремент на променлива

Това е сложна тема. Вече знаем, че – намалява стойността на целочислена променлива с единица. И ++ я увеличава с едно.

Пред инкремент ++n

++n първо прави n = n + 1; после ви дава новата стойност на n да се ползва;

Пост инкремент n++

n++ първо ви дава n да я ползвате. После смята n = n + 1;

#include <iostream>
using namespace std;

int main() {
    int j = 40;
    int k = 40;
    
    cout << j++ << endl;  // 40
    cout << ++k << endl;  // 41
}

По същата логика работят –n и n–.

6. Тернарен оператор

Това е съкратен запис на if() else конструкцията.

condition ? value1 : value2;

В наш превод:

(проверка) ? ( ако е true връща value1) : (ako e false връща value2);

Това се ползва за работа с бази данни от години. За да не се запише NULL в някое поле. Или просто ни дава логически избор каква стойност да присвоим на някаква променлива.

#include <iostream>
using namespace std;

int main() {
    int a = 10, b = 20;

    int max = (a > b) ? a : b;

    cout << "Max: " << max << endl;
}

Пример с cout.

#include <iostream>
using namespace std;

int main() {
    int x = 5;

    cout << (x > 0 ? "Positive" : "Negative or zero") << endl;
}

Избягвайте тернарния оператор в сложни конструкции или влагането на тернарни оператори. Кода става труден за четене.

7. Побитови (bitwise) оператори

Те заслужават отделна тема. Работят както логическите оператори но работят с битовете на числото. За сега няма да говорим за тях. Но знаем, че ги има.

8. Оператора запетая (,)

Операторът , (запетая) в C++ е малко подвеждащ. Той изпълнява няколко изразите последователно и връща стойността на последния. Този оператор идва от езика C.

int x = exp1, exp2, exp3;

Кода от горния пример ще изпълни първо exp1, после exp2. И на края exp3. На x ще се присвои стойността на exp3.

Kакво ще отпечата долната програма?

#include <iostream>
using namespace std;

int main() {
    int x = (1, 2, 3);
    cout << x << endl;
}
Отговор

3

Този оператор се използва и за деклариране на променливи. Но там запетаята е просто разделител. Не е оператор. Ето защо понякога начинаещите го бъркат.

double a, b, c;
int x, y, z = 3;

Горния запис е притеснителен. Той декларира локални променливи. Но само z е инициализирана. Използването на не инициализирани променливи води до недефинирано поведение.

Приоритет на операторите в C++ и асоциативност

Това е най – важната част от урока. Не я учете наизуст. Няма смисъл. Ползвайте таблицата за справка. Или потърсете в интернет, ако не сте сигурни за приоритета на операторите. И най важното: Винаги използвайте скоби, за да покажете на човека четящ кода ви как трябва да се пресметне израза според вас. Оставяйте интервали за по добра четимост.

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

Означава същото като в математиката. Първо се изчисляват операторите с най висок приоритет в израза и така нататък. Например умножение и деление са операции с по висок приоритет ос събиране и изваждане.

Асоциативност

А какво правим, когато имаме оператори с еднакъв приоритет? Тогава компилатора гледа тяхната асоциативност и извършва пресмятанията.

Ето и опростен модел на приоритетите.

ПриоритетОператориОписаниеАсоциативност
1 (най висок)() [] -> .Скоби, достъп до елемент/членляво → дясно
2++ -- ! ~ + - * & sizeofУнарни оператори
(тези с едни операнд)
дясно → ляво
3* / %Умножение, деление, остатъкляво → дясно
4+ -Събиране, изважданеляво → дясно
5< <= > >=Релационни (сравнения)ляво → дясно
6== !=Равенстволяво → дясно
7&&Логическо Иляво → дясно
8?:Тернарен оператордясно → ляво
9= += -= *= /= %=Присвояванедясно → ляво
10,Запетаяляво → дясно

Ето и пример. Умишлено сме премахнали скобите. Каква ще е стойността на result?

int result = 5 + 2 * 3 > 10 && 4 != 2;

Стъпки на пресмятане:

  • 2 * 3 = 6 -> result = 5 + 6 > 10 && 4 != 2;
  • 5 + 6 = 11 -> result = 11 > 10 && 4 != 2;
  • 11 > 10 → true -> result = true && 4 != 2;
  • 4 != 2 → true -> result = true && true
Отговор

result = true && true
result = 1

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

Забравяте скобите

int x = 5 + 2 * 3;   // = 11, не 21

Бъркате = (присвояване) там където трябва да е == (проверка за равенство)

if (x = 5)   // ❌ грешка (присвояване)

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

Забравяте за приоритети на || и &&.

if (true || false && false)

&& е с по висок приоритет. Пишете скоби, за да ясно.

Тернарен оператор без скоби

Става трудно за четене.

int x = true ? 1 : 2 + 3;   // = 1

Смесени сравнения

if (a < b < c)  // ❌ не е ясно

Горното ще се изпълни като:

(a < b) < c 

Кодът се пише веднъж, но се чете многократно.
Приоритетът е за компилатора – скобите са за хората.

Задачи:

1. Какви са стойностите на x, y и z?

bool x = (true || false) && false;
bool y = 12 > x && x < 20;
bool z = sizeof(double) == sizeof(long long); 
Отговор

x = false (0)
y = true (1)
z = true (1)

2. Какво ще отпечата следната програма?

#include <iostream>
using namespace std;

int main() {
    const int MAX = 20;
    int x = 0;
    while(x <= MAX) {
        if(x % 2 == 0)
            cout << x << " ";
        ++x;
    }
}
Отговор

Отпечатва четните числа от интервала [0; 20].

3. Какво прави следния израз?

int x = ...;
if(i >= 0 || i < size) { ... }
Отговор

Проверява дали i е в интервала [0; size);

4. Каква е стойността на x?

bool x = 5 > 3 && 2 < 1 || 4 == 4;
Отговор

x = true (1)

5. Каква е стойността на x?

int x = 5 < 3 ? 10 : 20;
Отговор

x = 20

6*. Каква е стойността на x?

int a = 5, b = 10, c = 15;
int x = (a + b > c) ? (a * b) : (b * c);
Отговор

x = 150

7*. Какъв ще е резултата от следния код?

#include <iostream>
using namespace std;

int main() {
    int i = 5;
    int x = i++ + ++i;
    cout << x << endl;
}
Отговор

Дори и да се компилира израза в ред 6 представлява Недефинирано поведение.
Избягвайте подобни двусмислени изрази.

8*. Пренапишете кода от задача 2, така че да се отпечатват само нечетните числа.

Отговор
#include <iostream>
using namespace std;

int main() {
    const int MAX = 20;
    int x = 0;
    while(x <= MAX) {
        if(x % 2 != 0)
            cout << x << " ";
        ++x;
    }
}

9*. Какво ще се отпечата на конзолата?

#include <iostream>
#include <cmath>
using namespace std;

int main() {
    float a = 16.4;
    float b = 3.14;
    float c = 9.0;
    
    float j = (trunc(a), cos(b), sqrtf(c));
    cout << "j = " << j << endl;
}
Отговор

j = 3


В скобите запетаята се тълкува като оператор. За това отговора е последната функция – квадратен корен от 9. Ако нямахме скоби резултата щеше да се тълкува така:

float j = trunc(a), 
      cos(b), 
      sqrtf(c);

Тоест, запетаята щеше да е просто разделител.