ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 11.12.2025

Просмотров: 3266

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

Скобки перекрывают приоритеты операторов, явно указывая, как именно компилятор должен интерпретировать выражение. С# ищет наиболее вложенную пару скобок и вы­ числяет выражение в ней — в данном случае это 6/3, что дает значение 2. В результате получается:

int п = (7 % 3) * (4 + 2) ; // 2 = 6 / 3

Затем С# продолжает поиск скобок и вычисляет значения в них, что приводит к выражению:

int

n =

1 * 6; / / 6 = 4 + 2, 1 = 7 % 3

Так что в конечном счете получается:

int

П =

6;

Правило "всегда используйте скобки" имеет, пожалуй, одно исключение. Лично мне с этим сложно примириться, но многие программисты опускают скобки в выражениях наподобие приведенного ниже, поскольку очевидно, что умножение имеет более высо­

кий приоритет, чем сложение:

 

int п = 7 + 2 * 3 ;

// То же, что и 7 + ( 2 * 3 )

Оператор п р и с в а и в а н и я

С# унаследовал одну интересную концепцию от С и С++: присваивание является би­ нарным оператором, возвращающим значение аргумента справа от него. Присваивание имеет тот же тип, что и оба аргумента (типы которых должны быть одинаковы).

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

П= 5 * 3 ;

Вданном примере 5*3 = 15 и имеет тип i n t . Оператор присваивания сохраняет это int-значение справа в int-переменной слева и возвращает значение 15. То, что он воз­ вращает значение, позволяет, например, сохранить это значение еще в одной перемен­ ной, т.е. написать:

m = п = 5 * 3 ;

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

Такое странное определение присваивания делает корректным такой причудли­ вый фрагмент, как показанный ниже (хотя я и предпочитаю воздерживаться от по­ добных вещей):

int n; int m;

П = m = 2 ;

Старайтесь избегать цепочек присваиваний, поскольку они менее понятны человеку, читающему исходный текст программы. Всего, что может запутать человека, читающего исходный текст вашей программы (включая и лично вас), следует избегать. Любые неяс­ ности ведут к ошибкам. Огромная часть программирования — от правил языка и его конструкций до соглашений по именованию переменных и рекомендаций, основанных на опыте программистов — нацелены на одно: устранение ошибок в программах.

[лава 4. Операторы

75


С# добавляет ко множеству простейших операторов небольшое подмножество опера­ торов, построенных на основе других бинарных операторов. Например, выражение

П += 1;

эквивалентно следующему:

П = П + 1 ;

Такие операторы присваивания существуют почти для всех бинарных операторов. I В табл. 4.2 показаны наиболее распространенные составные операторы присваивания.

Т а б л и ц а

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

Оператор

Значение

В табл. 4.2 опущено два более сложных составных оператора присваивания, <<= и >>=. Операторы побитового сдвига, на которых они основаны, будут рассмотрены позже в этой главе.

О п е р а т о р инкремента

Среди всех сложений, выполняемых в программах, добавление 1 к переменной — наиболее распространенная операция:

n = n + 1; // Увеличение п на 1

С# позволяет записать такую операцию сокращенно: n += 1; // Увеличение п на 1

Но, оказывается, и это недостаточно кратко, и в С# имеется еще более краткое обозначе­ ние этого действия — оператор инкремента:

++п ; // Увеличение п на 1

Все три приведенных выражения функционально эквивалентны, т.е. все они увеличи­ вают значение п на 1.

Оператор инкремента достаточно странен, но еще больше странности добавляет ему то, что на самом деле имеется два оператора инкремента: ++п и п++. Первый, ++п, на­ зывается префиксным, а второй, п++, — постфиксным. Разница между ними достаточно тонкая, но очень важная.

Вспомните, что каждое выражение имеет тип и значение. В следующем фрагменте

исходного текста и ++п, и п++ имеют тип int:

int П;

 

П = 1;

 

int р = ++П;

 

76

Часть II. Основы программирования в С#


n = 1;

int m = n + + ;

Чему равны значения p и m после выполнения этого фрагмента? (Подсказка: можно выбирать 1 или 2.) Оказывается, значение р равно 2, а значение m — 1. То есть значение выражения + + п — это значение п после увеличения, а значение п++ равно значению п до увеличения. Значение самой переменной п в обоих вариантах равно 2.

Эквивалентные операторы декремента— п- - и — п— используются для замены выражения n = n - 1 . Они работают точно так же, как и операторы инкремента.

Откуда взялся оператор инкремента?

Причина появления оператора инкремента лежит в туманном прошлом — в наличии в 1970-х годах в машине PDP-8 машинной команды инкремента.

Язык С, прямой предок С#, в свое время создавался для применения именно на этих машинах. Наличие соответствующей машинной команды позволяло уменьшить ко­ личество машинных команд при использовании п++ вместо п=пч-1. В то время эко­ номия даже нескольких машинных команд давала существенный выигрыш во вре­ мени работы.

В настоящее время компиляторы гораздо интеллектуальнее, и нет никакой разницы, написать ли в программе п++ или п=п +1 . Однако программисты — люди привычки, так что оператор инкремента благополучно дожил до сегодняшних дней, и увидеть в программе на С или С++ выражение п=п +1 практически нереально.

Кроме того, чаще всего программистами используется постфиксная версия оператора. Впрочем, это дело вкуса.. ?

С# предоставляет к услугам программиста также целый ряд логических операторов сравнения, показанных в табл. 4.3. Эти операторы называются логическими сравнениями (logical comparisons), поскольку они возвращают результат сравнения в виде значения true или f a l s e , имеющего тип b o o l .

• Вот примеры использования логических сравнений:

int m = 5; int n = 6;

bool b = m > n;

В этом примере переменной b присваивается значение f a l s e , поскольку 5 не боль­ ше, чем 6.

2 Использование префиксного оператора инкремента дает определенный выигрыш (компилятору не приходится дополнительно хранить значение переменной до инкремента), так что лучше приобретать привычку применять префиксную форму там, где выбор вида оператора инкремента не принципиален. — Примеч. ред.

Глава 4. Операторы

77


Т а б л и ц а 4.3.

Логические операторы сравнения

Оператор...

 

...возвращает true, если ...

 

 

 

 

а

=.= Ь

а

имеет то же значение, что и ь

а

> ь

а

больше ь

а

>= b

а больше или равно ь

а

<

ь

а

меньше ь

а

<=

ь

а меньше или равно ь

а

! = Ь

а

не равно ь

 

 

 

 

 

Логические сравнения определены для всех числовых типов, включая f l o a t , dou­ b l e , d e c i m a l и c h a r . Все приведенные ниже выражения корректны:

b o o l

 

b ;

 

 

b

=

3

>

2;

 

b

=

3

. 0

> 2 . 0 ;

b

=

'a1

>

'b ' ;

b

=

1 A'

<

' a ' ;

b

=•

' A1

<

'b ' ;

b =

10M

>

12M;

 

 

 

 

 

/ /

t r u e

 

 

 

 

/ /

t r u e

 

 

 

 

/ /

f a l s e

-

позже в алфавитном

порядке

/ /

означае т

"больше"

 

 

/ /

t r u e

-

прописное 'А'

меньше

/ /

строчного ' а '

 

 

/ /

t r u e

-

вс е прописные

буквы

меньше все х

/ /

строчных

 

 

 

/ /

f a l s e

 

 

 

 

Операторы сравнения всегда дают в качестве результата величину типа b o o l . Опера­ торы сравнения, отличные от ==, неприменимы к переменным тира s t r i n g (не волнуй­ тесь, С# предлагает другие способы сравнения строк).

С р а в н е н ие ч и с е л с п л а в а ю щ е й т о ч к о й

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

f l o a t

 

f l ;

 

 

 

 

 

f l o a t

f 2 ;

 

 

 

 

 

f l

=

1 0 ;

 

 

 

 

 

f2

=

f l

/

3 ;

 

 

 

 

b o o l

Ы

=

(3

*

f2)

==

f l ;

f l

=

9;

 

 

 

 

 

 

f2

=

f l

/

3 ;

 

 

 

 

b o o l

b2

=

(3

*

f2)

==

f l ;

Обратите внимание, что в пятой и восьмой строках примера сначала содержится опе­ ратор присваивания =, а затем оператор сравнения ==. Это — разные операторы. С# сначала выполняет логическое сравнение, а затем присваивает его результат пере­ менной слева от оператора присваивания.

Единственное отличие между вычислениями Ы и Ь2 состоит в исходном значении f 1. Так чему же равны значения Ы и Ь2? Очевидно, что значение Ы равно t r u e : 9/3 равно 3, 3*3 равно 9, 9 равно 9. Никаких проблем!

Значение Ы не столь очевидно:

10/3 равно 3.3333.... 3.3333...*3 равно 9.9999.... Но

равны ли числа 9.9999... и 10? Это

зависит от того, насколько сообразительны ваши

78

Часть II. Основы программирования в С#


компилятор и процессор. При использовании процессора типа Pentium С# недостаточно умен для того, чтобы понять, что Ы надо присвоить значение t r u e .

Для сравнения f 1 и f 2 можно воспользоваться функцией для вычисления аб­ солютного значения следующим образом:

M a t h . a b s ( f l - f 2 * 3 . 0 ) < . 0 0 0 1 ; // . . . и л и другая степень точ­ ности

Такая функция вернет значение t r u e в обоих случаях. Вместо . 0 0 0 1 можно исполь­ зовать константу D o u b l e . E p s i l o n , чтобы получить максимальную точность. Эта кон­ станта представляет собой наименьшую возможную разницу между двумя неравными значениями типа double .

Чтобы узнать, какие еще возможности скрывает в себе класс System . Math, вос­ пользуйтесь командой меню Help1 ^Index и введите Math в поле Look For.

Составные л о г и ч е с к и е о п е р а т о р ы

Для переменных типа b o o l имеются специфичные для них операторы, показанные в табл. 4.4.

Оператор ! представляет собой логический эквивалент знака "минус". Например, ! а истинно, если а ложно, и ложно, если а истинно.

Следующие два оператора тоже вполне просты и понятны. а&Ь истинно тогда и толь­ ко тогда, когда и а, и b одновременно равны t r u e ; а | b истинно тогда и только тогда, когда или а, или Ь, или оба они одновременно равны t r u e . Оператор Л (исключающее или) возвращает значение t r u e тогда и только тогда, когда значения а и b различны — т.е. когда одно из значений t r u e , а второе — f a l s e .

Все перечисленные операторы возвращают в качестве результата значение типа b o o l .

Операторы &, | и имеют версии, называющиеся побитовыми (bitwise). При применении к переменным типа i n t эти операторы выполняют действия с каж­ дым битом отдельно. Таким образом, 6&3 равно 2 (01102&00112 равно 00102), 6 | 3 равно 7 (011021 00112 равно 01112), а 6Л3 равно 5 (01102 Л 00112 равно

01012). Бинарная арифметика — очень интересная вещь, но она выходит за рам­ ки настоящей книги.

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

bool b = (ЛогическоеВыражение!) & (ЛогическоеВьгражение2) ;

Глава 4. Операторы

79