В предыдущей статье мы рассмотрели различные типы данных и их структуры. Это было достаточно познавательно, однако в отрыве от операций данные не имеют большого смысла. Кроме просто хранения данных мы хотим получать какие-то вычисляемые величины. Так, например, мы можем знать о всех произведенных покупках, и хотеть вычислить сколько же всего денег мы потратили. Или знать все оценки полученные за семестр и хотеть вычислить средний балл.










Арифметические операции


Обе приведенные выше задачи носят арифметический характер, так что мы в первую очередь поговорим об арифметических операциях, ведь они наиболее наглядны. Итак, в C++ есть операции +, -, *, /, % - это сложение, вычитание, умножение, деление и остаток от деления, соответственно. Тут следует учесть один факт, касательно операции деления, связанный с типами данных, являющийся не очевидным: частное двух целых числе всегда будет целым числом! Что это означает? Рассмотрим описанные выше операции на примерах:

1. 1+4=5
2. 3-9=-6
3. 5*9=45
4. 6/2=3, но 5/4=1. Как и указано выше, частное целых числе - целое число, если необходимо "точно" вычислить частное необходимо использовать числа с плавающей точкой(см. предыдущую статью).
5. 5.0/4.0=1.25. Почему я говорю "точно"? Потому что отношения некоторых(даже целых) чисел не может быть записано в конечной форме, так число две третьих представляется в десятичном виде, как бесконечная циклическая дробь. Что как легко догадаться не может быть записано в конечной памяти компьютера(к тому же, как было замечено в предыдущей статье, существуют некоторые ограничения на размер данных для каждого типа).
6. 3%2=1

Кроме вышеперечисленных операций(так называемых бинарных, то есть содержащих два аргумента), есть унарные(соответственно один аргумент) операции. Из арифметических унарных операций чаще всего используется унарный минус. Принцип его работы абсолютно очевиден:

1. -(5)=-5
2. -(-5)=5

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

Логические и побитовые операции.


Кроме арифметических в программировании часто используют логические операции. Это операции над булевыми величинами, то есть такими, которые принимают значения "истина" и "ложь". Наиболее распространенными являются логические операции "и", "или" и унарная операция "не". Математически доказано, что любая логическая операция может быть выражена с помощью этих трех(на самом деле, достаточно даже двух: "не" и "и" или "или"). Действуют эти операции следующим образом. Результат операции "и" "истина", тогда и только тогда, когда "истинны" оба ее аргумента. Результат операции "или" "ложь", тогда и только тогда, когда "ложны" оба ее аргумента. Результат операции "не" "истина", если ее аргумент "ложен", и "ложь" в противном случаи.
Как уже было замечено в предыдущей статье, в языке C++ нету булевогого типа данных. Как же тогда применять логические операции? Дело в том, что в языке C++, действует правило: все не нулевые числа являются "истинными", а ноль - "ложным". Это довольно необычная конвенция с точки зрения математики, но в программировании она используется повсеместно(так что ее придется запомнить). Рассмотрим примеры:

1. Логический оператор "и" в C++ записывается так: "&&". Выражение 1&&0 будем "ложным", поскольку второй аргумент "ложный", а выражение "истинно", лишь когда оба аргумента "истинны".
2. Логический оператор "или" записывается так: "||". Выражение 1||0 будет "истинным", поскольку первый оператор "истинный".
3. Логический оператор "не" записывается так:"!". Выражение !1 будет "ложным", поскольку аргумент операции "не" истинный.

Кроме логических операций "и", "или" и "не", существуют так же побитовые. Они записываются, соответственно, "&", "|" и "~". Чтоб объяснить принцип их действия, необходимо вначале объяснить в каком виде числа хранятся в памяти компьютера. А хранятся они, соответственно, по битам(бит - минимальная единица памяти, хранящая значение 0 или 1). Рассмотрим несколько примеров представления чисел в двоичной системе:

1. Число 5 в двоичной системе записывается, как 101. Поскольку 1*2^2+0*2^1+1*2^0=5(символом ^ я здесь и далее буду обозначать возведение в степень)
2. Число 13 в двоичной системе записывается так 1101. Разложение выглядит так 1*2^3+1*2^2+0*2^1+1*2^0=13.
3. Число 4 в двоичной системе записывается как 100. Попробуйте написать разложение сами.
4. Подумайте над тем, как будет записываться число 9?

Возвращаясь к вопросу побитовых операций, можно сказать, что это операции над двоичной формой числа. То есть если мы говорим, о выражении 13|5, то операции на самом деле производится над двоичными числами 1101 и 101. Результатом такой операции будет применение логической операции к каждому биту аргументов. В данном примере длинна числа 13 в двоичном представлении больше длинны числа 5, поэтому, для удобства, мы дополним число 5 лидирующим нулем и получим 0101. Результат от этого, конечно же, не поменяется (более того, на самом деле эти числа имеют примерно такой вид 0000000000001101 и 0000000000000101 соответственно, поскольку все числа одного типа занимают одно и то же количество памяти, то есть одинаковое количество бит, но об этом мы поговорим позже). Итак теперь мы можем применить наш оператор, результатом будет двоичное число 1101, которое, как мы знаем, записывается в десятичном виде как 13.
Действие побитовой операции "и", полностью аналогично, с тем лишь отличием, что к битам будет применятся логическая операция "и". Соответственно, 5&13 будет равно 5. Возникает вопрос, всегда ли результатом побитовых операций "и" и "или" будет один из аргументов? Ответ - нет. Это совпадение, например, 5|8=13, а 5&8=0.
Кстати, я забыл упомянуть, что и логические и побитовые операции "и" и "или" являются симметричными. Это означает, что перемена мест их операндов не влияет на результат(так же как для операций + и *).
Рассмотрим отдельно операцию побитового "не". Тут нам придется вернуться к рассказу о двоичной записи чисел. Во-первых, следует сказать, что для целых чисел со знаком, знак так же занимает один бит. Так, например, число 5, находящееся в переменной типа, со знаком, заснимающего один байт(8 бит) имеет следующий вид +0000101(один бит - знак, еще семь разряды числа, каждый со значением 0 или один). Если, например, число 5 находится беззнаковом типе, занимающем один байт, то его запись будет такой 00000101. В первом случаи побитовые отрицание числа 5 будет иметь двоичную запись -1111010, что в десятичном представлении записывается, как -122. Во втором же случае, отрицание числа 5 будет иметь вид 11111010, то есть в привычной нам записи 250. Как мы можем видеть, результат побитового отрицания сильно зависит от типа данных, а нее только от значения в нем.
Кроме перечисленных выше побитовые операций существуют так же "исключающее или" и "побитовые сдвиги". В целом побитовые операции используются в довольно специфических задачах, потому я не буду детально останавливаться на последних из приведенных операций. На данный момент моя цель - объяснить принцип выполнения этих операций, а на деталях остановимся позже, если это будет необходимо.
Возвращаясь к логическим операциям, хочется упомянуть о таких простых, но тем не менее весьма полезных из них, как "больше чем", "меньше чем", "равно" и их комбинациях. Возможно, вы раньше не задумывались над этим, но привычные нам еще с младшей школы "отношения", не что иное, как логические операции. Действительно, выражение a>b может быть "истинным" или "ложным", третьего, так сказать, не дано. В языке программирования C++ представлены следующие отношения-операции ">"(больше), ">="(больше или равно), "<"(меньше), "<="(меньше или равно), "=="(равно), "!="(не равно). Думаю, что как вычисляются отношения более-менее понятно, то есть, например, 5<3 -"ложно", а 2!=7 -"истинно".

Тернарный оператор "?:"


Мы уже рассматривали бинараные и унарные операции, настала очередь рассмотреть и тернарный(от трех аргументов). В C++, да и если меня не подводит память в других языках программирования используется ровно один тернарный оператор(мы уже видели различные бинарные и унарные операторы, но тернарный будет только один). Это оператор "?:", его применение записывается, как a?b:c. Результат выполнения этого оператора будет равен b, если выражение a "истинно" и c, если a "ложно". Это может показаться несколько заумно, но в некоторых ситуациях этот оператор бывает удобен. К примеру Вам необходимо обрабатывать значения больше нуля, но на вход Вы получаете произвольное целое число. С помощью тернарного оператора можно задать правило, по которому всем числам меньше нуля будет ставиться в соответствие 0, а прочим оно само. Записывается это следующим образом: (a>0)?a:0.
Кстати, функцию с картинки так же можно задать с помощью тернарного оператора. Подумайте, как это сделать.



Скобки


В предыдущем примере, были использованы две операцию одно выражении. Это достаточно типичная ситуация. В таких случаях, как и в математике, для задания порядка выполнения операций используются скобки. Так, например, умножение выполняется до сложения и вычитания. Для языка C++, существует исчерпывающая таблица приоритетов выполнения операций, ее можно найти в саночной литературе. Однако, по собственному опыту скажу, что очень мало кто из программистов помнит ее наизусть. Потому, рекомендую, в случаи когда есть сомнения использовать скобки (и конечно не, когда сомнений в том, что они нужны нет). Лишние скобки особо не мешают, н о могут спасти Вас от длительной откладки условий, а так же помочь не запутаться тому, кто будет читать код после Вас.
Это в целом все что я хотел рассказать сегодня об операторах. В следующей статье я расскажу о циклах, условных операторах и функциях. Всем удачи.
1

Комментарии

Для того, чтоб оставлять комментарии или зарегистрируйтесь.