Операции — это действия над данными. Данные, участвующие в операции, часто называют операндами. В качестве операнда может выступать константа, переменная или вызов какой-нибудь функции. Для каждой операции используется соответствующий ей знак операции, состоящий из одного или нескольких символов. В результате выполнения операций всегда получается какое-то значение, представляющее собой результат выполнения операции.
В языке С++ существует большое количество разнообразных операции. Их можно классифицировать по разным признакам, например: по количеству операндов, по назначению, либо как-то ещё.
В зависимости от количества операндов в C++ есть одноместные, двухместные и одна трёхместная операция.
По назначению операции можно сгруппировать таким образом: арифметические, операции сравнения, логические, побитовые, специальные.
Рассмотрим сейчас основные операции, а остальные (специального характера) будем разбирать позже, когда это станет необходимо. В прочем, в таблице ниже приведены все знаки операции, которые имеются в C++, и можно сразу получить о них некоторое представление. Операции в таблице разбиты на группы в соответствии с их рангами.
За исключением операций "[ ]", "( )" и "? :", все знаки операций распознаются компилятором как отдельные лексемы. В зависимости от контекста одна и та же лексема может обозначать разные операции, т.е. один и тот же знак операции может употребляться в различных выражениях и по-разному интерпретироваться в зависимости от контекста. Например, бинарная операция & — это поразрядная конъюнкция, а унарная операция & — это операция получения адреса.
Операции ранга 1 имеют наивысший приоритет. Операции одного ранга имеют одинаковый приоритет, и если их в выражении несколько, то они выполняются в соответствии с правилом ассоциативности либо слева направо (→), либо справа налево (←). Если один и тот же знак операции приведен в таблице дважды (например, знак *), то первое появление (с меньшим по номеру, т.е. старшим по приоритету) соответствует унарной операции, а второе — бинарной.
Приоритеты (ранги) операций
Ранг |
Операции |
Ассоциативность |
1 |
( ) [ ] -> :: . |
→ |
2 |
! ~ + - ++ -- & * new delete sizeof |
← |
3 |
typeid |
→ |
4 |
операторы приведения типа |
→ |
5 |
.* ->* |
→ |
6 |
* / % (мультипликативные бинарные) |
→ |
7 |
+ - (аддитивные бинарные) |
→ |
8 |
<< >> (поразрядного сдвига) |
→ |
9 |
< <= >= > (отношения) |
→ |
10 |
== != (отношения) |
→ |
11 |
& (поразрядная конъюнкция "И") |
→ |
12 |
^ (поразрядное исключающее "ИЛИ") |
→ |
13 |
| (поразрядная дизъюнкция "ИЛИ") |
→ |
14 |
&& (конъюнкция "И") |
→ |
15 |
|| (дизъюнкция "ИЛИ") |
→ |
16 |
? : (условная операция) |
← |
17 |
= *= /= %= += -= &= ^= |= <<= >>= |
← |
18 |
, (операция "запятая") |
→ |
Это наиболее часто используемые операции. Их смысл близок к тому, каким он известен из курса математики. Итак, перечислим их:
Знак операции |
Назначение |
Пример использования |
Результат |
+ |
сложение |
2+5 |
7 |
- |
вычитание |
4-1 |
3 |
* |
умножение |
3*5 |
15 |
/ |
деление (обычное) |
2.4/2 |
1.2 |
/ |
целочисленное деление |
5/2 |
2 |
% |
вычисление остатка при целочисленном делении |
5%2 |
1 |
Приоритет операций сложения и вычитания ниже, чем умножения, деления и вычисления остатка. Для изменения порядка вычисления используют круглые скобки, например для умножения на 2 суммы двух чисел A и B можно написать:
2*(A+B)
Далее. Как видно из полученных результатов, в C++ один знак / означает две разные операции. Если один или оба операнда — вещественные, то выполняется обычное деление, если оба операнда — целые, то выполняется деление нацело и результат будет целого типа. Использование этой операции требует повышенной внимательности, например, если запрограммировать вычисление математического выражения
буквально, т.е. так:
1/3*sin(2*X)
то результат вне зависимости от значения X всегда будет равен нулю, так как выражение 1/3 означает деление нацело. Для решения проблемы достаточно один из операндов сделать вещественным
1.0/3*sin(2*X)
Операция вычисления остатка (%) применима только для целочисленных операндов.
Смена знака. Унарная операция «-» означает смену знака. Как видно из общей таблицы всех операций, она имеет очень высокий приоритет — выше, чем, к примеру, у операции умножение. Поэтому в выражении
-A*B
вначале выполняется смена знака для A, а затем умножение -A на B.
Для парности имеется и операция унарный плюс, т.е. можно написать
+A
Для каких целей это использовать? Сложно сказать. Но такая возможность есть.
Более интересны, и главное, очень употребительны операции автоувеличения и автоуменьшения.
Операция автоувеличения (инкремент)
Записывается как два знака плюс (без пробелов между ними!), применяется для увеличения на единицу значения переменной, например:
A++
Исходное значение переменной A увеличивается на 1, и полученный результат сохраняется в переменной A. По полученному результату эта операция соответствует следующему выражению:
A=A+1
хотя на уровне машинных команд — это разные вещи. Операция автоувеличения эквивалентна аналогичной команде INC языка ассемблера. Имеет малый размер кода и выполняется быстрее обычного сложения. На старых процессорах типа Intel 8086 операция инкремента выполнялась в несколько раз быстрее, чем операция сложения.
Важный момент: автоувеличение применимо именно для переменной, но не для константы или выражения.
Для операции инкремент допустимы две формы записи:
префиксная — например, ++A
постфиксная — например, A++.
При префиксной форме записи делается увеличение переменной на 1 и затем используется новое значение этой переменной.
В постфиксной форме записи также переменная увеличивается на 1, но в текущем выражении используется старое значение переменной.
Рассмотрим это на примерах. Применим постфиксную форму записи. Результат:
Операции |
Значение переменной A |
Значение переменной B |
A=1 |
1 |
|
B=A++ |
2 |
1 |
Префиксная форма записи даёт такой результат:
Операции |
Значение переменной A |
Значение переменной B |
A=1 |
1 |
|
B=++A |
2 |
2 |
Как видим, переменная A в обоих случаях изменилась на 1 и равна 2, а вот для переменной B результаты получились различными.
Из приведённых примеров следует, что использование операции автоувеличения в выражениях требует от программиста вдумчивого отношения, необходимо чётко представлять, что хотим получить, и что получим в действительности.
Если операция автоувеличения используется как самостоятельный оператор, т.е. как предложение на языке C++, то между обеими формами нет ни какой разницы. Так, операторы
A++; // или
++A;
увеличивают значение переменной A и ничего более, т.е. при таком использовании они, по сути, эквивалентны.
Чаще всего инкремент используют в операторах цикла для увеличения на 1 переменной цикла.
Операция автоуменьшения (декремент)
Записывается как два знака минус (без пробелов между ними!), применяется для уменьшения на единицу значения переменной, например:
A--
Исходное значение переменной A уменьшается на 1, и полученный результат сохраняется в переменной A. По полученному результату эта операция соответствует следующему выражению:
A=A-1
Операция автоуменьшения также имеет две формы записи: постфиксная (A--) и префиксная (--A). В языка ассемблера есть соответствующая ей команда DEC. Это также одна из наиболее быстрых операций. Всё, что было сказано об особенностях работы операции автоувеличения, справедливо и для операции автоуменьшения.
Применяются для сравнения (сопоставления) числовой или символьной информации. Результатом выполнения операций является либо истина (true), либо ложь (false). Перечислим эти операции:
< — меньше;
<= — меньше или равно;
== — проверка на равенство (пишется два знака «равно» без пробелов);
!= — проверка на неравенство;
> — больше;
>= — больше или равно.
Приоритет этих операций ниже, чем у арифметических операций. Пример использования:
A+B>=C
Проверяем: сумма A и B больше С? Если «да», то ответом будет true, если «нет», то false.
В использовании операций сравнения нет ничего сложного. Но следует обратить внимание на одно обстоятельство.
Вещественные числа в памяти хранятся приближенно, при работе с ними в ходе выполнения арифметических операций могут накапливаться погрешности, поэтому не рекомендуется выполнять проверку на равенство или на неравенство для вещественных чисел, т.к. результат может быть непредсказуем. А если очень надо? Что делать?Поступаем просто. Если для вещественных чисел надо выполнить проверку на равенство (типа A==B), то заменяем её на проверку на неравенство вида
|A-B|<=ε,
где ε — некая малая положительная величина эпсилон.
В терминах языка C++ это будет выглядеть так
fabs(A-B)<=eps
Вот и всё решение проблемы. Единственно, не забудьте предварительно задать значение для эпсилон.
Логические операции тесно связаны с операциями сравнения и используются для построения сложных логических выражений. Имеется три логические операции:
Название |
Знак операции |
Пример записи |
Пояснение |
Логическое отрицание (НЕ) |
! |
!X |
Если X — истина, то результат — ложь и наоборот |
Логическое умножение (И) |
&& |
X && Y |
Результат — истина, если истинны оба операнда |
Логическое сложение (ИЛИ) |
|| |
X || Y |
Результат — истина, если истинен хотя один операнд |
Приоритет операций логического сложения и умножения ниже, чем у арифметических операций и операций сравнения. Зато логическое отрицание имеет очень высокий приоритет.
Пример. Проверить, является ли число A двухзначным. Возможное решение:
A>9 && A<100
Как известно, на компьютере в один приём, т.е. одной операцией, можно обрабатывать только данные размером в 1 байт или более. При этом в ряде случаев необходимо выяснить значения отдельных двоичных разрядов (битов). Для этих целей в языке C++ имеется большой набор операций, позволяющих выполнять различные действия над отдельными битами. Все побитовые операции допустимы только для целых типов. Перечислим эти операции:
Название |
Знак операции |
Пример записи |
Результат выполнения операции |
Поразрядное И |
& |
3&5 |
1 |
Поразрядное ИЛИ |
| |
3|5 |
7 |
Поразрядное исключающее ИЛИ |
^ |
3^5 |
6 |
Инверсия |
~ |
~3 |
-4 |
Поразрядный сдвиг влево |
<< |
5<<1 |
10 |
Поразрядный сдвиг вправо |
>> |
5>>1 |
2 |
При выполнении поразрядной операции И над соответствующими разрядами исходных операндов выполняется операция И. Она напоминает логическую операцию И, но только выполняется с отдельными битами. Если соответствующие биты в обоих операндах равны 1, то и соответствующие бит результата равен 1, иначе он равен 0. Побитовое выполнение этой операции представлено ниже:
|
Двоичная форма записи чисел |
|
Десятичная форма записи чисел |
||
& |
1 |
0 |
1 |
|
5 |
0 |
1 |
1 |
|
3 |
|
|
0 |
0 |
1 |
|
1 |
При выполнении поразрядной операции ИЛИ всё происходит аналогично, только над разрядами выполняется операция ИЛИ: результат равен 0, если оба соответствующих разряда равны 0, иначе результат равен 1.
Поразрядное исключающее ИЛИ даёт нам другое правило для вычисления разрядов результата: результат равен 1, если разряды разные, и равен 0, если они одинаковые.
Инверсия — это одноместная операция, заменяющая каждый бит операнда на обратный: 0 на 1, а 1 на 0. Обратите внимание: инверсия — это не смена знака операнда.
Поразрядный сдвиг влево выполняется для разрядов левого операнда на число позиций, равное правому операнду. По результату работы сдвиг влево на k позиций аналогичен умножению исходного числа на .
Поразрядный сдвиг вправо выполняется для разрядов левого операнда на число позиций, равное правому операнду. По результату работы сдвиг право на k позиций аналогичен целочисленному делению исходного числа на .
Операции сдвига работают быстрее, чем операции умножения или деления.
|
|
|