Выбрать главу

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 )

// сделать что-то ...

Упражнение 4.4

Найдите неправильные или непереносимые выражения, поясните. Как их можно изменить? (Заметим, что типы объектов не играют роли в данных примерах.)

(a) ptr-iva1 != 0

(с) ptr != 0 *ptr++

(e) vec[ iva1++ ] = vec[ ival ];

(b) ival != jva1 kva1 (d) iva1++ ival

Упражнение 4.5

Язык С++ не диктует порядок вычисления операций сравнения для того, чтобы позволить компилятору делать это оптимальным образом. Как вы думаете, стоило бы в данном случае пожертвовать эффективностью, чтобы избежать ошибок, связанных с предположением о вычислении выражения слева направо?

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-значением. Так, после определений

const int array_size = 8;

int ia[ array_size ] = { 0, 1, 2, 2, 3, 5, 8, 13 };

int *pia = ia;

выражение

array_size = 512; // ошибка

ошибочно, хотя array_size и является l-значением: объявление array_size константой не дает возможности изменить его значение. Аналогично

ia = pia; // ошибка

ia – тоже l-значение, но оно не может быть значением массива.

Неверна и инструкция

pia + 2=1; // ошибка

Хотя pia+2 дает адрес ia[2], присвоить ему значение нельзя. Если мы хотим изменить элемент ia[2], то нужно воспользоваться операцией разыменования. Корректной будет следующая запись:

*(pia + 2) = 1; // правильно

Операция присваивания имеет результат – значение, которое было присвоено самому левому операнду. Например, результатом такой операции

ival = 0;

является 0, а результат

ival = 3.14159;

равен 3. Тип результата – int в обоих случаях. Это свойство операции присваивания можно использовать в подвыражениях. Например, следующий цикл

extern char next_char();

int main()

{

char ch = next_char();

while ( ch != '\n' ) {

// сделать что-то ...

ch = next_char();

}

// ...

}

может быть переписан так:

extern char next_char();

int main()

{

char ch;

while (( ch = next_char() ) != '\n' ) {

// сделать что-то ...

}

// ...

}

Заметим, что вокруг выражения присваивания необходимы скобки, поскольку приоритет этой операции ниже, чем операции сравнения. Без скобок первым выполняется сравнение: