Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Операции сравнения и логические операции



Таблица 4.2. Операции сравнения и логические операции

Символ операции Значение Использование
! Логическое НЕ ! expr
< Меньше expr1 < expr2
< = Меньше или равно expr1 < = expr2
> Больше expr1 > expr2
> = Больше или равно expr1 > = expr2
== Равно expr1 == expr2
! = Не равно expr1! = expr2
& & Логическое И expr1 & & expr2
|| Логическое ИЛИ expr1 || expr2

Примечание. Все операции в результате дают значение типа bool

 

Операции сравнения и логические операции в результате дают значение типа bool, то есть true или false. Если же такое выражение встречается в контексте, требующем целого значения, true преобразуется в 1, а false – в 0. Вот фрагмент кода, подсчитывающего количество элементов вектора, меньших некоторого заданного значения:

vector< int>:: iterator iter = ivec.beg-in();

while ( iter! = ivec.end() ) {

// эквивалентно: e1em_cnt = e1em_cnt + (*iter < some_va1ue)

// значение true/false выражения *iter < some_va1ue

// превращается в 1 или 0

e1em_cnt += *iter < some_va1ue;

++iter;

}

Мы просто прибавляем результат операции “меньше” к счетчику. (Пара += обозначает составной оператор присваивания, который складывает операнд, стоящий слева, и операнд, стоящий справа. То же самое можно записать более компактно: elem_count = elem_count + n. Мы рассмотрим такие операторы в разделе 4.4.)

Логическое И (& & ) возвращает истину только тогда, когда истинны оба операнда. Логическое ИЛИ (||) дает истину, если истинен хотя бы один из операндов. Гарантируется, что операнды вычисляются слева направо и вычисление заканчивается, как только результирующее значение становится известно. Что это значит? Пусть даны два выражения:

expr1 & & expr2

expr1 || expr2

Если в первом из них expr1 равно false, значение всего выражения тоже будет равным false вне зависимости от значения expr2, которое даже не будет вычисляться. Во втором выражении expr2 не оценивается, если expr1 равно true, поскольку значение всего выражения равно true вне зависимости от expr2.

Подобный способ вычисления дает возможность удобной проверки нескольких выражений в одном операторе AND:

while ( ptr! = О & &

   ptr-> va1ue < upperBound & &

   ptr-> va1ue > = 0 & &

   notFound( ia[ ptr-> va1ue ] ))

{... }

Указатель с нулевым значением не указывает ни на какой объект, поэтому применение к нулевому указателю операции доступа к члену вызвало бы ошибку (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 )

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

Упражнение 4.4

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

(a) ptr-> iva1! = 0

(с) ptr! = 0 & & *ptr++

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

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

Упражнение 4.5

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

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

Инициализация задает начальное значение переменной. Например:

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;


Поделиться:



Последнее изменение этой страницы: 2019-04-09; Просмотров: 340; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.054 с.)
Главная | Случайная страница | Обратная связь