1.2. Операторы - StriderAJR/StudentCpp GitHub Wiki
Оператор - некая фраза в языке программирования, определяющая законченный этап обработки данных одного или двух операндов.
- Операторы выражения
- Приоритет операторов
- Приведение типов
- Условные операторы
- Оператор
if
- Оператор
switch
- Тернарный оператор
- Оператор
- Оператор
,
- Операторы цикла
- Ссылки на исходники
Если упростить еще сильнее, то оператор - это какое-то действие, которое может иметь один или два параметра.
int a = 5;
=
- это оператор. Оператор присваивания. Его операндами (параметрами) являются число 5
и переменная a
. Этот оператор инициализирует переменную a
числом 5
(в область памяти, помеченную именем a
, записывается число 5
).
Операторы могут состоять не только из одного символа. Существуют такие операторы, как if
, switch
, new
, delete
и т.д. Это тоже операторы. Но обо всем в свое время.
Это достаточно простой оператор, с которым мы уже работали, когда писали первую программу:
std::cout << "Hello, World!";
- Левый операнд оператора
::
- это верхний в иерархии объект, внутри которого что-то есть. - Правый операнд оператора
::
- это нижний в иерархии объект, который мы специализируем
namespace std
|-- cout
|-- cin
|-- что-то-еще
Если сформулировать еще проще: оператор ::
говорит в какой "коробке" мы ищем нужную нам вещь. И запись std::cout
расшифровывается как "внутри пространства std
найди объект cout
".
Все. Не больше и не меньше. Оператор ::
позволяет нам сказать точно, где находится тот или иной объект/функция.
Это бинарные операторы, т.е. им нужно 2 операнда (параметра) для работы, левый и правый.
-
+
- сложение (внезапно!) -
-
- вычитание (невероятно!) -
*
- умножение (кто бы мог подумать!) -
/
- целочисленное деление, дробная часть результата будет отброшена, даже если она должна была бы быть -
%
- остаток от деления
int a = 10;
cout << a + 1; // 11
cout << a - 1; // 9
cout << a * 2; // 20
cout << a / 3; // 3
cout << a % 3; // 1
Операторы инкремента и декремента содержат в себе оператор присваивания. Выражения
a++;
b--;
полностью эквивалентно записи
a = a + 1;
b = b - 1;
Но инкремент и декремент еще могут быть постфиксные и инфиксные. Постфиксный - оператор стоит после операнда. Инфиксиный - оператор стоит до операнда. Разницу между постфиксном и инфиксном проще показать на примере.
int a = 0;
int b = 0;
cout << a++ << endl; // выведется... 0
cout << ++b << endl; // выведется... 1
Сложно сказать, что вам покажется более странным, что в первом случае вывелся 0, или что во втором случае вывелось 1, или что вообще есть разница в результате для этих двух строк.
Разница между инфиксом и постфиксом в том, что произойдет раньше: инкремент/декремент или вы получите значение переменной. Давайте я просто распишу верхний алгоритм на составляющие:
int a = 0;
int b = 0;
cout << a << endl;
a = a + 1;
b = b + 1;
cout << b << endl;
Вот теперь отлично видно, что в случае с постфиксом, мы сначала используем операнд в своем выражении и только затем происходит инкремент.
В случае с инфиксом сначала происходит инкремент и только затем операнд участвует в выражении.
Эта разница бывает критичной в алгоритмах, поэтому стоит об этом помнить.
Логические операторы возвращают результатом своей работы логический тип данных bool
.
-
==
- проверка на равенство -
!=
- проверка на неравенство Остальные операторы, кажется, даже пояснять нужды нет.
-
&
- конъюнкция -
|
- дизъюнкция -
!
и~
- отрицание -
^
- исключающая дизъюнкция
Есть такие "веселые" парные операторы, которые вечно начинающие забывают разделять в своей голове на 2 разные подгруппы: логические и побитовые. У логической группы результатом будет тип bool
- истина или ложь, побитовая же группа операторов в качестве результата выдает число.
- Логическая группа это операторы
&&
,||
,!
int a = 10;
int b = 0;
bool c = true;
cout << (a && b) << endl; // 0
cout << (a && c) << endl; // 1
cout << (b && c) << endl; // 0
// Выражение пришлось записать в круглых скобках из-за приоритета операторов
// У оператора && приоритет ниже, чем у <<
Надеюсь, вы помните, почему вывод получился 0
и 1
- потому что в С++ логический тип данных все равно числа. Автоматически красивые слова true
и false
вам никто не выведет, это прерогатива уже более высокоуровневых языков типа С#.
- Побитовая группа это операторы
&
,|
,~
Особенность побитовых арифметических операторов, что они работают с двоичным представлением числа и причем с каждым отдельным битом, а не со всем числом в целом. Результат получается целочисленное число.
int a = 3;
int b = 4;
bool c = 1;
cout << (a & b) << endl;
cout << (a | b) << endl;
cout << (a ^ b) << endl;
cout << (~c) << endl;
Если записать побитого каждое число, то
a = 0011
b = 0100
c = 0001
0011 & 0100 = 0000 = 0
0011 | 0100 = 0111 = 7
0011 ^ 0100 = 0111 = 7
Так что не нужно путать пары: &
и &&
, |
и ||
- это разные операторы с разным результатом и даже разным типом данных результата.
Это тоже побитовые операторы. Они сдвигают число на нужное кол-во разрядов вправо или влево.
int a = 1;
cout << (a << 2) << endl; // 4
0001 << 2 = 0100 = 4
И помните, что cout << "Hello"
и a << 2
- это один и тот же оператор побитого сдвига. Но в дальнейшем мы изучим, что поведение оператора можно менять для разных типов данных. Поэтому оператор <<
для потока ввода/вывода не сдвигает что-то там влево, а записывает или читает из потока данные. Но оператор и там, и там - это оператор побитого сдвига, но его поведение изменили именно для потока. Это называется "перегрузить оператор" и будет изучаться в дальнейшем.
В С++ есть комбинированные операторы, которые образовались как краткая запись самого оператора вместе с присваиванием.
a = a+5;
можно записать также как
a += 5;
Эти строчки будут полностью эквивалентны.
И таких "сокращенных" операторов очень много:
a -= 1; // a = a-1;
a *= 2; // a = a*2;
a /= 3; // a = a / 3;
a %= 4; // надеюсь, дальше мне уже не нужно расписывать?
a <<= 5;
a >>= 6;
a &= 7;
a ^= 8;
a |= 9;
TODO
int a = 5;
int b = 7;
int c = a+b;
В последней строке применяется сразу 2 оператора. Сначала выполняется оператор +
, а затем оператор =
. Почему именно в таком порядке? Потому что в любом языке программирования, также как в математических выражениях, у операторов есть приоритет.
Например, оператор +
имеет больший приоритет чем оператор =
. Именно поэтому сначала вычислится сумма двух переменных: a
и b
и только затем результат присвоится переменной c
.
int a = 5;
int b = 7;
int c = a+b*a; // c будет равно 40, а не 60
Полный перечень операторов с их приоритетами можно найти в любом справочнике или тематической сайте, например, CppReference - Приоритет операций
Условные операторы еще назваются операторами ветвления, потому что они позволяют "разветвить" алгоритм - создать несколько веток, которые будут выполняться при выполнении определенного условия.
Например, если заданное условие будет истинным, то выполнить алгоритм №1, а если ложно, то выполнить алгоритм №2.
Некоторые операторы ветвлений позволяют создать только 2 ветки алгоритмов: истинную и ложную (оператор if
), а некоторые операторы могут создать сразу множество веток (оператор switch
).
Как уже было сказано оператор if
проверяет заданное условие и выполняет либо одну, либо другую ветку алгоритма в зависимости от истинности условия.
if(условие)
{
// код, который выполняется, если условие было истинным
}
else
{
// код, который выполняется, если условие было ложным
}
Обратите внимание, что условие это не обязательно какое-то просто выражение типа
a == 10
или
a > 0
Это могут и сложные конструкции, состоящие из нескольких действий.
if( ((a > 10) && (b == 3)) || (a % 2 == 0) )
cout << "True";
else
cout << "False";
Обратите внимание, в языке С++ истиной является все, что отлично от нуля, а ложью является только 0. Так что вполне себе можно писать так:
if(10) cout << "Ok";
if(5 & 0) cout << 0;
Как видно, условие не обязательно должно иметь тип данных bool
.
А еще оператор if
как несложно догадаться можно быть вложенным.
if(a == 5) cout << "Excellent!";
else if(a == 4) cout << "Good";
else if(a == 3) cout << "Not so good";
else if(a == 2) cout << "Bad";
else if(a == 1) cout << "You are blunt or what?";
else if(a == 0) cout << "WTF?";
else cout << "0_o";
В коде выше в зависимости от значения переменной a
будет выведено только одно сообщение. Если же переменная a
не будет задана ни одному из предустановленных значений, то выведется конечный вариант-реакция на неизвестное значение. Так можно организовать такой алгоритм с помощью вложенных операторов if
.
Но записать тот же самый алгоритм можно и с помощью другого оператора switch
- множественного ветвления.
Тот же самый код выше можно записать и другим образом.
switch(a)
{
case 5: cout << "Excellent!"; break;
case 4: cout << "Good"; break;
case 3: cout << "Not so good"; break;
case 2: cout << "Bad"; break;
case 1: cout << "You are blunt or what?"; break;
case 0: cout << "WTF?"; break;
default: cout << "0_o";
}
Этот код полностью идентичен по логике выполнения тому, что был записан с помощью вложенных операторов if
. Считается, что в случае множественного ветвления оператор switch
более компактен и предпочтителен к применению.
В целом синтаксис оператора switch
такой:
switch(переменная_значение_которой_проверяется)
{
case вариант_1:
// код который нужно выполнить для варианта 1
break;
case вариант_2:
// код который нужно выполнить для варианта 1
break;
// таких вариантов может быть сколько угодно
case вариант_n:
// код который нужно выполнить для варианта n
break;
default:
// код, который нужно выполнить, если ни один из вариантов не подойдет
}
Ветка default
может и отстутствовать, ее писать не обязательно. Тогда в случае отсутствия совпадений, в рамках оператора switch
ничего не будет выполонено.
И последний важный момент про оператор switch
: это оператор прерывания выполнения break
. Если не писать этот оператор в конце алгоритма для каждой ветки, то особенность работы switch
такова, что будет выполнен код, который идет сразу следом за выполняющимся.
int a = 2;
switch(a)
{
case 2:
cout << "Two";
case 1:
cout << "One";
}
Если вы забыли про break
, то ожидаете, что на экран выведется
Two
Но только это не так. На самом деле выведется
TwoOne
Потому что такова особенность switch
. Без break
начнется выполняться следующая ветка.
Казалось, зачем было так тупо делать, сделали бы разработчики С++, чтобы ветка прерывалась автоматически. Но на самом деле это бывает полезно, если вам нужно собирать предустановленные значение в группы.
switch(a)
{
case 5:
case 4:
cout << "You are a good student!";
break;
case 3:
cout << "Bad student!"
break;
case 2:
case 1:
cout << "You're not a student anymore.";
}
Если бы не особенность работы switch
, то вывод сообщения пришлось бы повторять для вариантов 5 и 2. А это дублирование кода. Создавать функцию для такой ерунды тоже не лучшее решение. Так что такая организация работы switch
не так уж и плоха.
Тернарный оператор это краткий вариант записи оператора if
. Тернарным его зовут, потому что он состоит из трех частей: условия, выражения для истины и выражения для лжи. Общая запись такая:
условие ? выражение_если_истина : выражение_ксли_ложь;
Возьмем пример оператора if
:
if(a > 0) cout << "Positive";
else cout << "Negative";
Его можно записать через тернарный оператор, например, вот так:
a > 0 ? cout << "Positive" : cout << "Negative";
Суть работы тернарного оператора в том, что после проверки условия вместо всего оператора останется либо выражение для истины, либо выражение для лжи. В приведенном коде выше, если переменная а
будет равна, скажем, 1, то вместо всего тернарного оператора останется только выражение cout << "Positive";
Именно поэтому тернарный оператор можно использовать в качестве inline
проверки - проверки прямо на строке с исполняемым кодом. Например, тот же код выше, который мы рассматриваем, можно записать и так:
cout << (a > 0 ? "Positive" : "Negative");
Тут мы прямо внутри команды cout <<
делаем проверку на положительность переменной a
и подсовываем либо константу Positive
, либо Negative
. Вместо всего тернарного оператора останется только одна, нужная, константа.
Примечание: пришлось взять тернарный оператор в круглые скобки, потому что приоритет у оператора >
выше, чем у <<
.
Так что развлекаться с тернарным оператором можно много.
int a = 10;
int b = a > 0 ? a / 2 : -a;
А еще тернарный оператор может быть вложенным. :)
cout << (a > 0
? "Positive"
: a == 0
? "Zero"
: "Negative");
Еще более веселый оператор - это оператор ,
. Его особенность в том, что он разделяет несколько выражений, но итогом (т.е. тем, что остается после его выполнения как результат) является только последнее выражение.
Это как в случае с тернарным оператором. После выполнения оператора, что-то должно "остаться" в строке кода. Лучше всего будет смотреть на примерах:
int a = 0;
int b = a+1, a+2, a++, a;
Этот же код можно было бы записать следующим образом:
int a = 0;
a+1; // Здесь нет сохранения данных. Число 1 будет "висеть в воздухе", сохранения в память нет
a+2; // То же самое, только с числом 2
a++; // А вот это краткая запись выражения a = a+1 - тут есть сохранение, переменная а стала равно 1
int b = a; // В итоге b будет присвоено значени 1.
Самое главное, что вместо всех вариантов в операторе ,
остался на строчке с присваиванием переменной b
значения последнее из перечисленных значений.
int b = a;
А теперь подумайте что выведется на экран:
int a = 10;
cout << (a /= 2, a++, a-5, a*3);
Правильный ответ: 18.
Операторов циклов есть 3 варианта: for
, while
и do while
. Для циклов будет выделен отдельный раздел в следующих заметках.