Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЛР5-С++-22 марта-2012.doc
Скачиваний:
12
Добавлен:
15.09.2019
Размер:
1.48 Mб
Скачать

1.8.3. Не путайте операторы равенства и присвоения

Предупреждение: не путайте знак операции присваивания = и знак операции сравнения!!!

То, что оператор присвоения можно использовать в условии, приводит иногда к удивительным результатам.

if (i = 42)

Этот код вполне допустим: здесь переменной i присваивается значение 42, кото­рое затем используется при проверке условия. Поскольку значение 42 отлично от нуля, оно интерпретируется как значение true. Однако автор этого кода почти на­верняка предполагал проверить равенство содержимого переменной i значению 42: if (i == 42)

Найти подобную ошибку довольно трудно. Правда, некоторые компиляторы, но не все, могут предупредить о подобном коде.

1.8.4. Составные операторы присвоения

В языке С++ также существует операция комбинированного (составного) присваивания. Общая синтаксическая форма составного оператора присвоения имеет следующий вид

V1 OP = V;

что эквивалентно

V1 = V1 OP V;

где V1 – переменная, V – выражение, OP – одна из 10 составных операций:

+ = - = *= /= %= // арифметические операторы

<<= >>= &= ^ = |= // побитовые операторы

Например, оператор а += b; эквивалентен оператору а = а + b; Составной оператор присвоения += добавляет правый операнд к левому, а ре­зультат сохраняет в левом операнде.

Обычная и составная формы присваивания имеют одно очень важное различие: в составном операторе при­своения левый операнд вычисляется только один раз. При использовании эквива­лентной, но более длинной версии левый операнд вычисляется дважды: один раз как правый операнд, а затем как левый. В подавляющем большинстве случаев это различие несущественно, возможно, кроме тех, где производительность критически важна.

В таблице 5.1 приведены все операции присваивания и примеры их использования.

Таблица 5.1

Операции присваивания языка С++

Знак

Операция и пример использования

=

Присвоить значение выражения-операнда из правой части операнду левой части: р =10.3 - 2*х;

*=

Присвоить операнду левой части произведение значений обоих операндов: р *= 2 эквивалентно р = р * 2;

/=

Присвоить операнду левой части частное от деления значения левого операнда на значение правого: р /= 2.2 – d эквивалентно р = р / (2.2 - d);

%=

Присвоить операнду левой части остаток от деления целочисленного значения левого операнда на целочисленное значение правого операнда: n %= 3 эквивалентно n = n % 3;

+=

Присвоить операнду левой части сумму значений обоих операндов а +=b в эквивалентно a = а + b;

-=

Присвоить операнду левой части разность значений левого и правого операндов: х -= 4.3 - z эквивалентно х = х - (4.3 - z);

<<=

Присвоить целочисленному операнду левой части значение, полученное сдвигом влево его битового представления на количество разрядов, равное значению правого целочис­ленного операнда: а <<= 4 эквивалентно а = а << 4;

>>=

Присвоить целочисленному операнду левой части значение, полученное сдвигом вправо его битового представления на количество разрядов, равное значению правого целочис­ленного операнда: а >>= 4 эквивалентно а = а >> 4;

&=

Присвоить целочисленному операнду левой части значение, полученное поразрядной конъюнкцией (И) его битового представления с битовым представлением целочисленного операнда правой части: е &= 44 эквивалентно е = в & 44;

|=

Присвоить целочисленному операнду левой части значение, полученное поразрядной дизъюнкцией (ИЛИ) его битового представления с битовым представлением целочисленного операнда правой части: а |=b эквивалентно а = а | b;

^=

Присвоить целочисленному операнду левой части значение, полученное применением поразрядной операции исклю­чающего ИЛИ к битовым представлениям значений обоих операндов: z ^= х + у эквивалентно z = z^ (х + у).

Иллюстрацией некоторых особенностей выполнения операций присваивания служит программа, приведенная в примере 5.8.

Пример 5.8.

/*Программа LR5-Primer8*/

//Операции присваивания

#include<iostream.h>

#include<conio.h>

int main()

{

int k;

cout << "\n\n k = 35/4 равняется " << (k=35/4);

cout << "\t k /= 1 + 1 + 2 равняется "<< (k/=1+1+2)<< endl;

cout << "\n k *= 5 - 2 равняется " << (k*=5-2);

cout << "\t k %= 3 + 2 равняется " << (k%=3+2)<< endl;

cout << "\n k += 21/3 равняется " << (k+=21/3);

cout << "\t k -= 6 - 6/2 равняется " << (k-=6-6/2)<< endl;

cout << "\n k <<= 2 равняется " << (k<<=2);

cout << "\t k >>= 6-5 равняется " << (k>>=6-5)<< endl;

cout << "\n k &= 9 + 4 равняется " << (k&=9+4);

cout << "\t k |= 8 - 2 равняется " << (k|=8-2)<< endl;

cout << "\n k ^= 10 равняется " << (k^=10)<< endl;

getch();

return 0;

}

В первом присваивании обратите внимание на выполнение деле­ния целочисленных операндов, при котором выполняется округление за счет отбрасывания дробной части результата.

Результаты выполнения:

Полученные числовые значения, во-первых, подтверждают экви­валентность записей El ор= Е2 и El = El op (Е2). Кроме того, ана­лизируя результаты, можно еще раз рассмотреть особенности поразрядных операций. Двоичный код для к, равного 5, будет 101. Сдвиг влево на 2 дает 10100 (десятичное 20). Затем сдвиг на 1 вправо формирует код 1010 (десятичное 10). Поразрядная конъюнкция 101061101 дает 1000 (десятичное 8). Затем 1000|100 дает значение 1110 (десятичное 14). Результатом 1110^1010 будет 0100 (десятичное 4).

1.8.5. L-значения и r-значения

Более подробная информация о выражениях приведена в главе 5, "Выражения", а пока отметим, что выражения языка С++ имеют две части.

1. L-значение (lvalue). Выражение, являющееся l-значением, может располагаться как с левой, так и с правой стороны оператора присвоения.

2. R-значение (rvalue). Выражение, являющееся r-значением, может располагаться только с правой, но никак не с левой стороны оператора присвоения.

Переменные, которые являются l-значениями, могут располагаться как с левой, так и с правой стороны оператора присвоения, а числовые литералы, являющиеся r-значениями, подлежат лишь присвоению, их значение изменить нельзя. Предполо­жим, что определено несколько переменных.

int units_sold = 0;

double sales_price = 0, total__revenue = 0;

Код, приведенный ниже, содержит ошибки, которые проявятся во время компи­ляции.

//ошибка: арифметическое выражение не является l-значением

units_sold * sales__price = total__revenue;

// ошибка: литеральная константа не является 1-значением

0 = 1;

Некоторые операторы, например оператор присвоения, требуют, чтобы один из его операндов был l-значением. Поэтому l-значения применяются значительно чаще, чем r-значения. Способ использования l-значения определяет контекст, в котором оно присутствует в выражении. Рассмотрим пример.

units_sold = units_sold + 1;

В этом выражении переменная units__sold используется как операнд двух раз­ных операторов. Оператору + нужны только значения его операндов. Значением пе­ременной (value) называют текущее значение, хранимое в области памяти, принадлежащей данной переменной. Оператор сложения получает эти значения, а вычис­ленную сумму возвращает как результат.

Переменная units_sold используется как левая сторона оператора присвоения (=). Оператор присвоения читает выражение с правой стороны и заносит его резуль­тат в переменную, указанную с левой стороны. В этом выражении результат сложе­ния сохраняется в области памяти, которая принадлежит переменной units_sold. Прежнее значение переменной units_sold при этом перезаписывается.

Операции присваивания.

В качестве левого операнда в операциях присваивания может ис­пользоваться только модифицируемое /-значение - ссылка на некото­рую именованную область памяти, значение которой доступно изменениям. Термин /-значение {left value), иначе - леводопустимое выражение, происходит от объяснения действия операции присваива­ния е - d, в которой операнд е слева от знака операции присваива­ния может быть только модифицируемым /-значением. Примером модифицируемого /-значения служит имя переменной, которой выде­лена память и соответствует некоторый класс памяти.