Одноместные и двуместные операции
С++ поддерживает обычный набор арифметических операций:
– умножение (*);
– деление (/)
– целочисленное деление (%);
– сложение (+);
– вычитание (–).
С++ поддерживает одноместный минус (a + (-b)), который выполняет двоичное дополнение, как в расширении ANSI. Кроме этого, С++ поддерживает одноместный плюс (a + (+b)).
4.5.3. Операции приращения (++) и уменьшения (--)
В С++ имеются некоторые специальные одноместные и двуместные операции. Наиболее известными являются одноместные операции приращения (++) и уменьшения (--). Они позволяют вам использовать единственную операцию, которая добавляет 1 или вычитает 1 из любого значения; сложение и вычитание может быть выполнено в середине выражения, причем вы можете даже решить, сделать это до или после вычисления выражения. Рассмотрим следующие строки программы:
sum = a + b++;
sum = a + ++b;
Первая означает: “сложить a и b, присвоить результат sum и увеличить b на единицу”.
Вторая означает: “увеличить b на единицу, сложить a и b, и присвоить результат sum”.
Это очень мощные операции, расширяющие возможности языка, однако перед их использованием нужно убедиться, что вы хорошо понимаете их действие. Измените SUM.C, как показано ниже, а затем, перед ее запуском, попытайтесь определить, что она выведет:
void main()
{
int a, b, sum;
char *format;
format = "a = %d b = %d sum = %d \n”;
a = b = 5;
sum = a +b; printf(format, a, b, sum);
sum = a++ +b; printf(format, a, b, sum);
sum = ++a +b; printf(format, a, b, sum);
sum = - - a +b; printf(format, a, b, sum);
sum = a -- +b; printf(format, a, b, sum);
sum = a +b; printf(format, a, b, sum);
}
Побитовые операции
Для обработки на уровне битов С++ имеет следующие операции:
– сдвиг влево (<<);
– сдвиг вправо (>>);
– И (&);
– ИЛИ (|);
– исключающее ИЛИ (^);
– НЕ (~).
Они позволяют вам производить операции очень низкого уровня. Для того, чтобы понять эффект этих операций, введите и выполните следующую программу:
void main();
{
int a, b, c;
char *format1, *format2;
format1 = "%04X %s %04X = %04X\n";
format2 = "%c%04X = %04X\n";
a = 0xOFFO; b = 0xFFOO;
c = a<<4; printf(format1, a, "<<", 4, c);
c = a>>4; printf(format1, a, ">>", 4, c);
c = a & b; printf(format1, a, "&", b, c);
c = a | b; printf(format1, a, "|", b, c);
c = a ^ b; printf(format1, a, "^", b, c);
c = ~ a; printf(format2, "~", a, c);
c = - a; printf(format2, "-", a, c);
}
Опять же, попробуйте предугадать то, что будет выводить эта программа, не запуская ее. Заметим, что спецификаторы ширины поля выравнивают выводимые значения; спецификатор %04X указывает на использование нулей в начале числа, на ширину поля вывода четыре цифры и на шестнадцатеричное представление (основание 16).
Комбинированные операции
С++ позволяет использовать некоторые сокращения при написании выражений, содержащих многочисленные операции, описанные выше (одноместные, двуместные, приращение, уменьшение и побитовые). Так, любое выражение вида
<переменная> = <переменная> <операция> <выражение>;
может быть заменено на
<переменная> <операция> = <выражение>;
Ниже приводятся некоторые примеры таких выражений и способы их сокращения:
a = a + b; сокращается до 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; сокращается до a <<= b;
a = a >> b; сокращается до a >>= b;
a = a & b; сокращается до a &= b;
a = a | b; сокращается до a |= b;
a = a ^ b; сокращается до a ^= b;.
Адресные операции
С++ поддерживает две специальные адресные операции: операцию определения адреса (&) и операцию обращения по адресу (*).
Операция & возвращает адрес данной переменной; если sum является переменной типа int, то &sum является адресом (расположения в памяти) этой переменной. С другой стороны, если msg является указателем на тип char, то *msg является символом, на который указывает *msg. Введите следующую программу и посмотрите, что получится.
void main
{
int sum;
char &msg;
sum = 5 +3;
msg = "Hello, there\n";
printf("sum = %d &sum = %p \n", sum, &sum);
printf("*msg = %c msg = %p \n", *msg, msg);
}
В первой строке печатается два значения: значение sum (8) и адрес sum (назначаемый компилятором). Во второй строке также печатается два значения: символ, на который указывает msg (H), и значение msg, которое является адресом этого символа (также назначен компилятором).
Условные операторы
Имеется несколько операторов, о которых мы еще не говорили: условные и логические операторы. При этом возникают некоторые непростые моменты в выражениях, которые мы приберегли для обсуждения условных (true или false – истина или ложь) операторов.
Операции сравнения
Операции сравнения позволяют сравнивать два значения, получая результат в зависимости от того, дает ли сравнение истину или ложь. Если сравнение дает ложь, то результирующее значение равно нулю; если значение истина, то результат равен 1. Вот список операций С++ для сравнения:
> больше;
>= больше или равно;
< меньше;
<= меньше или равно;
== равно;
!= не равно.
Почему нас должно заботить, является ли нечто истиной или ложью? Загрузите и выполните программу RATIO.C и посмотрите, что произойдет, когда вы введете 0 для второго значения. Программа напечатает сообщение об ошибке (“Divide by zero” – “Деление на ноль”) и остановится. Теперь сделайте следующие изменения в программе и запустите ее снова.
void main ()
{
float a, b, ratio;
printf ("Введите два числа: ");
scanf ("%f %f", &a, &b);
if (b == 0.0)
printf ("Отношение не определено.\n");
else
{
ratio= a / b;
printf ("Отношение = %f\n", ratio);
}
}
Оператор, находящийся в двух следующих за оператором scanf строках известен как условный оператор if. Вы можете понимать его так: “Если значение выражения (b == 0.0) истинно, сразу вызвать printf. Если значение выражения ложно, присвоить a/b переменной ratio, затем вызвать printf”.
Теперь, если вы введете 0 в качестве второго значения, то ваша программа напечатает сообщение “Отношение не определено”.
Если второе значение ненулевое, то программа вычисляет и печатает ratio.
Логические операции
Имеется также три логические операции: И (&&), ИЛИ (||) и НЕ (!). Их не следует путать с описанными выше битовыми операциями (&, |, ~). Логические операции работают с логическими значениями (истина или ложь) и позволяют составлять логические выражения.
Как же отличать их от соответствующих битовых операций? Вот так:
– логические операторы всегда дают в результате значение либо 0 (ложь), либо 1 (истина), в то время как поразрядные операторы выполняются путем последовательной обработки цепочки битов до тех пор, пока не станет ясен результат;
– логические операторы && и !! известны как операторы типа “short circuit”. Выполнение операторов такого типа прекращается как только становится ясно, будет ли результат иметь значение истина или ложь. Предположим, что вы имеете выражение вида:
exp1 && exp2
Если exp1 – ложь, значит и все выражение – ложь. Таким образом, exp2 никогда не будет вычисляться. Аналогично, если мы имеем выражение вида
exp1 !! exp2,
то exp2 никогда не будет вычисляться, если exp1 верно.
Прежде чем перейти к обсуждению операторов цикла, мы дадим некоторые комментарии к использованию выражений. Такие выражения, как (b == 0.0) и (a <= q*r) довольно привлекательны по своей красоте. Однако С++ допускает написание более сложных и запутанных конструкций, чем эти. Даже значительно более сложных.
Операторы присваивания
Любой оператор присваивания, заключенный в круглые скобки, является выражением с определенным значением, которое получается в результате этого присваивания.
Например, выражение (sum = 5+3) имеет значение 8, поэтому выражение ((sum =5+3) <= 10) будет всегда иметь значение “истина” (т.к. 8 < 10). Более экзотичен следующий пример:
if((ch=getch()) == ‘q’)
puts("До свидания! Программа завершена.\n");
else
puts("Продолжаем работу! \n");
Как работает эта конструкция? Если в программе используется выражение ((ch=getch()) == ‘q’);, то она, дойдя до него, останавливается и переходит в состояние ожидания ввода символа с клавиатуры. После того, как вы введете символ, осуществляется присваивание введенного символа переменной ch и выполняется сравнение введенного символа с символом q. Если введенный символ равен q, то на экран будет выведено сообщение “До свидания! Программа завершена.”, в противном случае будет выведено сообщение “Продолжаем работу!”.
4.6.4. Разница между присваиванием (=) и равенством (==)
В языках Паскаль и Бейсик проверка на равенство производится выражением:
if (a = b) puts ("Равно");
else puts ("Не равно");
Если это программа на Паскале или Бейсике, то вы можете предполагать, что будет напечатано “Равно”, если a и b имеют одинаковое значение и “Не равно” в противном случае.
Иначе происходит с программой на С++, где выражение a = b означает “Присвоить значение b переменной a”, и все выражение принимает значение b. Поэтому во фрагменте, приведенном выше, присвоится значение b переменной a, а затем напечатается “Равно”, если b имеет нулевое значение, в противном случае – “Не равно”.
Правильное решение следующее:
if (a == b) puts("Равно");
else puts("Не равно");
Оператор запятая
Вы можете использовать оператор запятая (,) для организации множественных выражений, расположенных внутри круглых скобок. Выражение внутри скобок вычисляется слева направо и все выражение принимает значение, которое было вычислено последним. Например, если oldch и ch имеют тип char, то выражение:
(oldch = ch, ch = getch());
присваивает переменной oldch значение ch, затем считывает символ, вводимый с клавиатуры, и запоминает его в ch. Результатом всего выражения, в итоге, будет значение введенного с клавиатуры символа. Приведем еще один пример:
ch=‘a’;
if((oldch = ch, ch = "b") == "a")
puts("Это символ ‘a’ \n");
else
puts("Это символ ‘b’\n");
Как вы считаете, какое сообщение будет выведено на экран вашего дисплея в результате выполнения приведенной выше программы?
Оператор if
Обратимся теперь опять к оператору if, который фигурировал при рассмотрении первых примеров. Оператор if имеет следующий основной формат:
if (значение)
оператор 1;
else
оператор 2;
где “значение” является любым выражением, которое приводится или может быть приведено к целочисленному значению. Если “значение” отлично от нуля (“истина”), то выполняется “оператор 1”, в противном случае выполняется “оператор 2”.
Дадим пояснение относительно двух важных моментов по использованию оператора if–else.
Во-первых, часть “else оператор2” является необязательной частью оператора if; другими словами, правомерно употребление следующей формы оператора if:
if (значение)
оператор 1;
В этой конструкции “оператор 1” выполняется тогда и только тогда, когда “значение” отлично от нуля. Если “значение” равно нулю, “оператор 1” пропускается и программа продолжает выполняться дальше.
Во-вторых, что делать, если вы хотите выполнить более одного оператора в зависимости от того, ложно или истинно выражение, указанное в операторе if? Ответ: используйте составной оператор if.
Составной оператор состоит из:
– левой или открывающей фигурной скобки ({);
– последовательности операторов, разделенных между собой точкой с запятой (;);
– правой или закрывающей фигурной скобки (}).
В приведенном ниже примере в предложении if используется один оператор, а в предложении else – составной оператор.
if (b == 0.0)
printf("Отношение не определено\n");
else
{
ratio = a/b;
printf("Значение отношения равно %f\n", ratio);
}
Вы можете так же заметить, что тело вашей программы (функция main) является подобием составного оператора if.
Условный оператор (?:)
В некоторых случаях необходимо произвести выбор между двумя альтернативами (и результирующими значениями), основанный на некотором условии. Обычно это реализуется оператором if...else, например, так:
int imin(int a, int b)
{
if (a < b) return(a);
else return(b);
}
Но, как оказывается, для реализации такого выбора достаточно одной специальной конструкции. Ее формат следующий:
выражение 1? выражение 2: выражение 3
А смысл таков: “если выражение 1 верно, то вычисляется выражение 2 и все выражение получает его значение; иначе вычисляется выражение 3 и передается его значение”. Используя эту конструкцию, imin можно представить следующим образом:
int imin(int a, int b)
{
return((a < b)? a: b);
}
Более того, можно даже записать imin как строку макроса:
#define imin(a,b) ((a < b)? a: b)
Теперь, где бы ваша программа не встретила выражение imin(e1, e2);, она замещает его на ((e1 < e2)? e1: e2) и продолжает вычисления. Это в действительности наиболее общее решение, так как a и b больше не ограничены типом int; они могут быть любого типа, с которым можно выполнить операцию сравнения.