Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Липпман.doc
Скачиваний:
8
Добавлен:
14.08.2019
Размер:
7.54 Mб
Скачать

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' ) {

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

}

// ...


}

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

next_char() != '\n'

и его результат, true или false, присваивается переменной ch. (Приоритеты операций будут рассмотрены в разделе 4.13.)

Аналогично несколько операций присваивания могут быть объединены, если это позволяют типы операндов. Например:

int main ()

{

int ival, jval;

ival = jval = 0; // правильно: присваивание 0 обеим переменным


// ...

}

Обеим переменным ival и jval присваивается значение 0. Следующий пример неправилен, потому что типы pval и ival различны, и неявное преобразование типов невозможно. Отметим, что 0 является допустимым значением для обеих переменных:

int main ()

{

int ival; int *pval;

ival = pval = 0; // ошибка: разные типы


// ...

}

Верен или нет приведенный ниже пример, мы сказать не можем, , поскольку определение jval в нем отсутствует:

int main()

{

// ...

int ival = jval = 0; // верно или нет?

// ...


}

Это правильно только в том случае, если переменная jval определена в программе ранее и имеет тип, приводимый к int. Обратите внимание: в этом случае мы присваиваем 0 значение jval и инициализируем ival. Для того чтобы инициализировать нулем обе переменные, мы должны написать:

int main()

{

// правильно: определение и инициализация

int ival = 0, jval = 0;

// ...


}

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

int arraySum( int ia[], int sz )

{

int sum = 0;

for ( int i = 0; i < sz; ++i )

sum = sum + ia[ i ];

return sum;


}

Для более компактной записи С и С++ предоставляют составные операции присваивания. С использованием такого оператора данный пример можно переписать следующим образом:

int arraySum( int ia[], int sz )

{

int sum = 0;

for ( int i =0; i < sz; ++i )

// эквивалентно: sum = sum + ia[ i ];

sum += ia[ i ];

return sum;


}

Общий синтаксис составного оператора присваивания таков:

a op= b;

где op= является одним из десяти операторов:

+= -= *= /= %=

<<= >>= &= ^= |=

Запись a op= b в точности эквивалентна записи a = a op b.

Упражнение 4.6

Найдите ошибку в данном примере. Исправьте запись.

int main() {

float fval;

int ival;

int *pi;

fval = ival = pi = 0;


}

Упражнение 4.7

Следующие выражения синтаксически правильны, однако скорее всего работают не так, как предполагал программист. Почему? Как их изменить?

(a) if ( ptr = retrieve_pointer() != 0 )

(b) if ( ival = 1024 )


(c) ival += ival + 1;