Язык программирования C++. Пятое издание - Стенли Липпман
Шрифт:
Интервал:
Закладка:
Унарный оператор минус возвращает отрицательную копию (возможно, преобразованную) значения своего операнда.
int i = 1024;
int k = -i; // i равно -1024
bool b = true;
bool b2 = -b; // b2 равно true!
В разделе 2.1.1 упоминалось, что значения типа bool не нужно использовать для вычислений. Результат -b — хороший пример того, что имелось в виду.
Для большинства операторов операнды типа bool преобразуются в тип int. В данном случае значение переменной b, true, преобразуется в значение 1 типа int (см. раздел 2.1.2). Это (преобразованное) значение преобразуется в отрицательное, -1. Значение -1 преобразуется обратно в тип bool и используется для инициализации переменной b2. Поскольку значение инициализатора отлично от нуля, при преобразовании в тип bool его значением станет true. Таким образом, значением b2 будет true!
Внимание! Переполнение переменной и другие арифметические особенностиНекоторые арифметические выражения возвращают неопределенный результат. Некоторые из этих неопределенностей имеют математический характер, например деление на нуль. Причиной других являются особенности компьютеров, например, переполнение, происходящее при превышении вычисленным значением размера области памяти, представленной его типом.
Предположим, тип short занимает на машине 16 битов. В этом случае переменная типа short способна хранить максимум значение 32767. На такой машине следующий составной оператор присвоения приводит к переполнению.
short short_value = 32767; // максимальное значение при short 16 битов
short_value += 1; // переполнение
cout << "short_value: " << short_value << endl;
Результат присвоения 1 переменной short_value непредсказуем. Для хранения знакового значения 32768 требуется 17 битов, но доступно только 16. Многие системы никак не предупреждают о переполнении ни во время выполнения, ни во время компиляции. Подобно любой ситуации с неопределенностью, результат оказывается непредсказуем. На системе авторов программа завершилась с таким сообщением:
short value: -32768
Здесь произошло переполнение переменной: предназначенный для знака разряд содержал значение 0, но был заменен на 1, что привело к появлению отрицательного значения. На другой системе результат мог бы быть иным, либо программа могла бы повести себя по-другому, включая полный отказ.
Примененные к объектам арифметических типов, операторы +, -, * и / имеют вполне очевидные значения: сложение, вычитание, умножение и деление. Результатом деления целых чисел является целое число. Получаемая в результате деления дробная часть отбрасывается.
int ival1 = 21/6; // ival1 равно 3; результат усекается
// остаток отбрасывается
int ival2 = 21/7; // ival2 равно 3; остатка нет;
// результат - целочисленное значение
Оператор % известен как остаток (remainder), или оператор деления по модулю (modulus). Он позволяет вычислить остаток от деления левого операнда на правый. Его операнды должны иметь целочисленный тип.
int ival = 42;
double dval = 3.14;
ival % 12; // ok: возвращает 6
ival % dval; // ошибка: операнд с плавающей запятой
При делении отличное от нуля частное позитивно, если у операндов одинаковый знак, и отрицательное в противном случае. Прежние версии языка разрешали округление отрицательного частного вверх или вниз; однако новый стандарт требует округления частного до нуля (т.е. усечения).
Оператор деления по модулю определен так, что если m и n целые числа и n отлично от нуля, то (m/n)*n + m%n равно m. По определению, если m%n отлично от нуля, то у него тот же знак, что и у m. Прежние версии языка разрешали результату выражения m%n иметь тот же знак, что и у m, причем на реализациях, у которых отрицательный результат выражения m/n округлялся не до нуля, но такие реализации сейчас запрещены. Кроме того, за исключением сложного случая, где -m приводит к переполнению, (-m)/n и m/(-n) всегда эквивалентны -(m/n), m%(-n) эквивалентно m%n и (-m)%n эквивалентно -(m%n). А конкретно:
21 % 6; /* результат 3 */ 21 / 6; /* результат 3 */
21 % 7; /* результат 0 */ 21 / 7; /* результат 3 */
-21 % -8; /* результат -5 */ -21 / -8; /* результат 2 */
21 % -5; /* результат 1 */ 21 / -5; /* результат -4 */
Упражнения раздела 4.2Упражнение 4.4. Расставьте скобки в следующем выражении так, чтобы продемонстрировать порядок его обработки. Проверьте свой ответ, откомпилировав и отобразив результат выражения без круглых скобок.
12 / 3 * 4 + 5 * 15 + 24 % 4 / 2
Упражнение 4.5. Определите результат следующих выражений:
(а) -30 * 3 + 21 / 5 (b) -30 + 3 * 21 / 5
(с) 30 / 3 * 21 % 5 (d) -30 / 3 * 21 % 4
Упражнение 4.6. Напишите выражение, чтобы определить, является ли значение типа int четным или нечетным.
Упражнение 4.7. Что значит переполнение? Представьте три выражения, приводящих к переполнению.
4.3. Логические операторы и операторы отношения
Операторам отношения передают операторы арифметического типа или типа указателя, а логическим операторам — операнды любого типа, допускающего преобразование в тип bool. Все они возвращают значение типа bool. Арифметические операнды и указатели со значением нуль рассматриваются как значение false, а все другие как значение true. Операнды для этих операторов являются r-значениями, а результат — r-значение.
Таблица 4.2. Логические операторы и операторы отношения
Порядок Оператор Действие Применение Правосторонний ! Логическое NOT !выражение Левосторонний < Меньше выражение < выражение Левосторонний <= Меньше или равно выражение <= выражение Левосторонний > Больше выражение > выражение Левосторонний >= Больше или равно выражение >= выражение Левосторонний == Равно выражение == выражение Левосторонний != Не равно выражение != выражение Левосторонний && Логическое AND выражение && выражение Левосторонний || Логическое OR выражение || выражение Операторы логического AND и ORОбщим результатом оператора логического AND (&&) является true, если и только если оба его операнда рассматриваются как true. Оператор логического OR (||) возвращает значение true, если любой из его операндов рассматривается как true.
Операторы логического AND и OR всегда обрабатывают свой левый операнд перед правым. Кроме того, правый операнд обрабатывается, если и только если левый операнд не определил результат. Эта стратегия известна как вычисление по сокращенной схеме (short-circuit evaluation).
• Правая сторона оператора && вычисляется, если и только если левая сторона истинна.