Указатель с нулевым значением не указывает ни на какой объект, поэтому применение к нулевому указателю операции доступа к члену вызвало бы ошибку (ptr-value). Однако, если ptr равен 0, проверка на первом шаге прекращает дальнейшее вычисление подвыражений. Аналогично на втором и третьем шагах проверяется попадание величины ptr-value в нужный диапазон, и операция взятия индекса не применяется к массиву ia, если этот индекс неправилен.
Операция логического НЕ дает true, если ее единственный оператор равен false, и наоборот. Например:
bool found = false;
// пока элемент не найден
// и ptr указывает на объект (не 0)
while ( ! found ptr ) {
found = 1ookup( *ptr );
++ptr;
}
Подвыражение
! found
дает true, если переменная found равна false. Это более компактная запись для
found == false
Аналогично
if ( found )
эквивалентно более длинной записи
if ( found == true )
Использование операций сравнения достаточно очевидно. Нужно только иметь в виду, что, в отличие от И и ИЛИ, порядок вычисления операндов таких выражений не определен. Вот пример, где возможна подобная ошибка:
// Внимание! Порядок вычислений не определен!
if ( ia[ index++ ] ia[ index ] )
// поменять местами элементы
Программист предполагал, что левый операнд оценивается первым и сравниваться будут элементы ia[0] и ia[1]. Однако компилятор не гарантирует вычислений слева направо, и в таком случае элемент ia[0] может быть сравнен сам с собой. Гораздо лучше написать более понятный и машинно-независимый код:
if ( ia[ index ] ia[ index+1 ] )
// поменять местами элементы
++index;
Еще один пример возможной ошибки. Мы хотели убедиться, что все три величины ival, jval и kval различаются. Где мы промахнулись?
// Внимание! это не сравнение 3 переменных друг с другом
if ( ival != jva1 != kva1 )
// do something ...
Значения 0, 1 и 0 дают в результате вычисления такого выражения true. Почему? Сначала проверяется ival != jval, а потом итог этой проверки (true/false – преобразованной к 1/0) сравнивается с kval. Мы должны были явно написать:
if ( ival != jva1 ival != kva1 jva1 != kva1 )
// сделать что-то ...
Найдите неправильные или непереносимые выражения, поясните. Как их можно изменить? (Заметим, что типы объектов не играют роли в данных примерах.)
(a) ptr-iva1 != 0
(с) ptr != 0 *ptr++
(e) vec[ iva1++ ] = vec[ ival ];
(b) ival != jva1 kva1 (d) iva1++ ival
Язык С++ не диктует порядок вычисления операций сравнения для того, чтобы позволить компилятору делать это оптимальным образом. Как вы думаете, стоило бы в данном случае пожертвовать эффективностью, чтобы избежать ошибок, связанных с предположением о вычислении выражения слева направо?
4.4. Операции присваивания
Инициализация задает начальное значение переменной. Например:
int ival = 1024;
int *pi = 0;
В результате операции присваивания объект получает новое значение, при этом старое пропадает:
ival = 2048;
pi = iva1;
Иногда путают инициализацию и присваивание, так как они обозначаются одним и тем же знаком =. Объект инициализируется только один раз – при его определении. В то же время операция может быть применена к нему многократно.
Что происходит, если тип объекта не совпадает с типом значения, которое ему хотят присвоить? Допустим,
ival = 3.14159; // правильно?
В таком случае компилятор пытается трансформировать тип объекта, стоящего справа, в тип объекта, стоящего слева. Если такое преобразование возможно, компилятор неявно изменяет тип, причем при потере точности обычно выдается предупреждение. В нашем случае вещественное значение 3.14159 преобразуется в целое значение 3, и это значение присваивается переменной ival.
Если неявное приведение типов невозможно, компилятор сигнализирует об ошибке:
pi = ival; // ошибка
Неявная трансформация типа int в тип указатель на int невозможна. (Набор допустимых неявных преобразований типов мы обсудим в разделе 4.14.)
Левый операнд операции присваивания должен быть l-значением. Очевидный пример неправильного присваивания:
1024 = ival; // ошибка
Возможно, имелось в виду следующее:
int value = 1024;
value = ival; // правильно
Однако недостаточно потребовать, чтобы операнд слева от знака присваивания был l-значением. Так, после определений