Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Программирование на языке Си

..pdf
Скачиваний:
15
Добавлен:
12.11.2023
Размер:
17.16 Mб
Скачать

52 Программирование на языке Си

х+++Ь эквивалентно (х++)+Ь z---- d эквивалентно (z—)-d

Отношения и логические выражения. Отношение опредетяется как пара арифметических выражений, соединенных 'разделенных) знаком операции отношения. Знаки операций отношения (уже были введены выше):

==

равно;

!=

неравно;

<

меньше, чем;

<=

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

>

больше, чем;

>=

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

Примеры отношений:

а-Ь>6.3 (х-4) *3=12

б<=44

Логический тип в языке Си отсутствует, поэтому принято, что отношение имеет ненулевое значение (обычно 1), если оно истинно, и равно 0, если оно ложно. Таким образом, значением отношения 6<=44 будет 1.

Операции >, >=, <, <= имеют один ранг 6 (см. табл. 1.4). Операции сравнения на равенство = = и ! = также имеют одина­ ковый, но более низкий ранг 7, чем остальные операции отно­ шений. Арифметические операции имеют более высокий ранг, чем операции отношений, поэтому в первом примере для выра­ жения а-b не нужны скобки. .

Логических операций в языке Си три:

!- отрицание, т.е. логическое НЕ (ранг 2)

&&- конъюнкция, т.е. логическое И (ранг 77);

I I - дизъюнкция, т.е. логическое ИЛИ (ранг 12).

Они перечислены по убыванию старшинства (ранга). Как правило, логические операции применяются к отношениям. До выполнения логических операций вычисляются значения отно­ шений, входящих в логическое выражение. Например, если а, Ь, с - переменные, соответствующие длинам сторон треугольника, то для них должно быть истинно, т.е. не равно 0, следующее логическое выражение:

а+Ь>с && а+с>Ь && Ь+с>а

Глава 1. Базовые понятия языка

53

Несколько операций одного ранга выполняются слева напра­ во, причем вычисления прерываются, как только будет опреде­ лена истинность (или ложность) результата, т.е. если в рассмотренном примере а+b окажется не больше с, то осталь­ ные отношения не рассматриваются - результат ложен.

Так как значением отношения является целое (0 или 1), то ничто не противоречит применению логических операций к це­ лочисленным значениям. При этом принято, что любое ненуле­ вое положительное значение воспринимается как истинное, а ложной считается только величина, равная нулю. Значением !5 будет 0, значением 4 && 2 будет 1 и т.д.

Присваивание (выражение и оператор). Как уже говори­ лось, символ "=" в языке Си обозначает бинарную операцию, у которой в выражении должно быть два операнда - левый (модифицируемое именующее выражение - обычно перемен­ ная) и правый (обычно выражение). Если z - имя переменной, то

z = 2.3 + 5.1

есть выражение со значением 7.4. Одновременно это значение присваивается и переменной z. Только В том случае, когда в конце выражения с операцией присваивания помещен символ это выражение становится оператором присваивания. Таким

образом,

z = 2.3 + 5.1;

есть оператор простого присваивания переменной z значения, равного 7.4.

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

Так как выражение справа от знака '=' может содержать, в свою очередь, операцию присваивания, то в одном операторе присваивания можно присвоить значения нескольким перемен-

54

Программирование на языке Си

ным, т.е. организовать "множественное" присваивание, напри­ мер:

с = х = d = 4.0 + 2.4;

Здесь значение 6.4 присваивается переменной d, затем 6.4 как значение выражения с операцией присваивания "d=4.0+2.4" присваивается х и, наконец, 6.4 как значение выражения "x=d" присваивается с. Естественное ограничение - слева от знака '=' в каждой из операций присваивания может быть только леводо­ пустимое выражение (в первых главах книги - имя перемен­ ной).

В языке Си существует целый набор "составных операций присваивания" (ранг 14 в табл. 1.4). Как уже говорилось в §1.4, каждая из составных операций присваивания объединяет неко­ торую бинарную логическую или арифметическую операцию и собственно присваивание. Операция составного присваивания является основой оператора составного присваивания:

имя_переменной ор=выражение;

где ор - одна из операций *, /, %, +, -, &, Л, |, « , » . Если рассматривать конструкцию "ор=" как две операции, то вначале

выполняется ор, а затем Например,

х*=2; z+=4; i/=x+4*z;

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

Таким образом, первый пример можно рассматривать как обозначение требования "удвоить значение переменной х"; вто­ рой пример - "увеличить на 4 значение переменной z"; третий пример - "уменьшить значение переменной i в (x+4*z) раз". Этим операторам эквивалентны такие операторы простого при­ сваивания:

х=х*2; z=z+4; i=i/(x+4*z) ;

Глава 1. Базовые понятия языка

55

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

а—Ъ/а; x=z%x.

Приведение типов. Рассматривая операцию деления, мы отметили, что при делении двух целых операндов результат по­ лучается целым. Например, значением выражения 5/2 будет 2, а не 2.5. Для получения вещественного результата нужно выпол­ нять деление не целых, а вещественных операндов, например, записав 5.0/2.0, получим значение 2.5.

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

int n=5, к=2;

double d;

int m;

d-(double) n/ (double) k; m=n/k;

В этом фрагменте значением d станет величина 2.5 типа double, а значением переменной m станет целое значение 2.

Операция деления является только одной из бинарных опе­ раций. Почти для каждой из них операнды могут иметь разные типы. Однако не всегда программист должен а явном виде ука­ зывать преобразования типов. Если у бинарной операции опе­ ранды имеют разные типы (а должны в соответствии с

56

Программирование на языке Си

синтаксисом выражения иметь один тип), то компилятор вы­ полняет преобразование типов автоматически, т.е. приводит оба операнда к одному типу. Например, для тех же переменных зна­ чение выражения d+k будет иметь тип double за счет неявного преобразования, выполняемого автоматически без указания программиста. Рассмотрим правила, по которым такие приведе­ ния выполняются.

Правила преобразования типов. При вычислении выраже­ ний некоторые операции требуют, чтобы операнды имели соот­ ветствующий тип, а если требования к типу не выполнены, принудительно вызывают выполнение нужных преобразований. Та же ситуация возникает при инициализации, когда тип ини­ циализирующего выражения приводится к типу определяемого объекта. Напомним, что в языке Си присваивание является би­ нарной операцией, поэтому сказанное относительно преобразо­ вания типов относится и ко всем формам присваивания, однако при присваиваниях значение выражения из правой части всегда приводится к типу переменной из левой части, независимо от соотношения этих типов.

Правила преобразования в языке Си для основных типов оп­ ределены стандартом языка. Эти стандартные преобразования включают перевод "низших" типов в "высшие".

Среди преобразований типов выделяют:

преобразования - в арифметических выражениях-,

преобразования при присваиваниях;

преобразования указателей.

Преобразование типов указателей будет рассмотрено в гла­ ве 4. Здесь рассмотрим преобразования типов при арифметиче­ ских операциях и особенности преобразований типов при прис­ ваиваниях. .

При преобразовании типов нужно различать преобразования, изменяющие внутреннее представление данных, и преобразова­ ния, изменяющие только интерпретацию внутреннего представ­ ления. Например, когда данные типа unsigned int переводятся в тип int, менять их внутреннее представление не требуется - из­ меняется только интерпретация. При преобразовании значений

Глава 1. Базовые понятия языка

57

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

Рассмотрим последовательность выполнения преобразования операндов в арифметических выражениях.

1. Все короткие целые типы преобразуются в типы не мень­ шей длины в соответствии с табл. 1.5. Затем оба значения, уча­ ствующие в операции, принимают одинаковый тип в соот­ ветствии со следующими ниже правилами.

2.Если один из операндов имеет тип long double, то второй тоже будет преобразован в long double.

3.Если п. 2 не выполняется и один из операндов есть double, другой приводится к типу double.

4.Если п. 2 - 3 не выполняются и один из операндов имеет тип float, то второй приводится к типу float.

5.Если п. 2 - 4 не выполняются (оба операнда целые) и один операнд unsigned long int, то оба операнда преобразуются к ти­ пу unsigned long int.

6.Если п. 2 - 5 не выполняются и один операнд есть long, другой преобразуется к типу long.

7.Если п. 2 - 6 не выполняются и один операнд unsigned, то другой преобразуется к типу unsigned.

8.Если п. 2 - 7 не выполнены, то оба операнда принадлежат типу int.

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

58

Программирование на языке Си

На рис. 1.2 стрелками отмечены "безопасные" арифметиче­ ские преобразования, гарантирующие сохранение точности и неизменность численного значения.

 

Та бл ица 1.5

Правила стандартных арифметических преобразований

Исходный тип Преобразованный

• Правила преобразований

тип

 

char

int

unsigned char

int

signed char

int

short

int

unsigned short

unsigned int

eniim

int

битовое поле

int

signed char

short

int

1

long

Расширение нулем или знаком в за­ висимости от умолчания для char

Старший байт заполняется нулем Расширение знаком Сохраняется то же значение Сохраняется то же значение Сохраняется то же значение Сохраняется то же значение

 

unsigned

 

char

 

4

float

unsigned

short

 

 

. . I

double

unsigned

int

4

 

long

unsigned

double

long

Рис. 1.2. Арифметические преобразования типов, гарантирующие сохранение значимости

Глава 1. Базовые понятия языка

59

При преобразованиях, которые не отнесены схемой (рис. 1.2) к безопасным, возможны существенные информационные поте­ ри. Для оценки значимости таких потерь рекомендуется прове­ рить обратимость преобразования типов. Преобразование цело­ численных значений в вещественные осуществляется настолько точно, насколько это предусмотрено аппаратурой. Если кон­ кретное целочисленное значение не может быть точно пред­ ставлено как вещественное, то младшие значащие цифры теряются и обратимость невозможна.

Приведение вещественного значения к целому типу выпол­ няется за счет отбрасывания дробной части. Преобразование целой величины в вещественную также может привести к поте­ ре точности.

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

~- поразрядное отрицание (дополнение или инвертиро­ вание битов) (ранг 2)\

»- сдвиг вправо последовательности битов (ранг 5)\

«- сдвиг влево последовательности битов (ранг 5);

А- поразрядное исключающее ИЛИ (ранг 9);

|

- поразрядное ИЛИ (поразрядная дизъюнкция) (ранг

&- поразрядное И (поразрядная конъюнкция) (ранг 8).

Операция поразрядного отрицания (дополнения или инвер­ тирования битов) обозначается символом и является унар­ ной (одноместной), т.е. действует на один операнд, который должен быть целого типа. Значение операнда в виде внутренне­ го битового представления обрабатывается таким образом, что формируется значение той же длины (того же типа), что и опе­ ранд. В битовом представлении результата содержатся 1 во всех разрядах, где у операнда 0, и 0 в тех разрядах, где у операнда 1. Например:

unsigned char Е='\0301', F; F=~E;

60 Программирование на языке Си

Значением F будет восьмеричный код '\076' символа '>' (см. Приложение 1). Действительно, битовые представления значе­ ний Е и F можно изобразить так:

11000001 - для значения переменной Е, т.е. для '\030Г

00111110 - для значения переменной F, т.е. для '\076'

За исключением дополнения, все остальные поразрядные операции бинарные (двухместные).

Операции сдвигов » (вправо) и « (влево) должны иметь целочисленные операнды. Над битовым представлением значе­ ния левого операнда выполняется действие - сдвиг. Правый операнд определяет величину поразрядного сдвига. Например:

5« 2 будет равно 20 5>>2 будет равно 1

Битовые представления тех же операций сдвига можно изо­ бразить так:

101« 2 равно 10100, т.е. 20;

101» 2 равно 001, т.е. 1.

При сдвиге влево на N позиций двоичное представление ле­ вого операнда сдвигается, а освобождающиеся слева разряды заполняются нулями. Такой сдвиг эквивалентен умножению значения операнда на 2N .

Сдвиг вправо на N позиций несколько сложнее. Тут следует отметить две особенности, Первое - это исчезновение младших разрядов, выходящих за разрядную сетку. Вторая особенность - отсутствие стандарта на правило заполнения освобождающихся левых разрядов. В стандарте языка сказано, что когда левый операнд есть целое значение с отрицательным знаком, то при сдвиге вправо заполнение освобождающихся левых разрядов определяется реализацией. Здесь возможны два варианта: осво­ бождающиеся разряды заполняются значениями знакового раз­ ряда (арифметический сдвиг вправо) или освобождающиеся слева разряды заполняются нулями (логический сдвиг вправо).

При положительном левом операнде сдвиг вправо на N пози­ ций эквивалентен уменьшению значения левого операнда в 2N

Глава 1. Базовые понятия языка

61

раз с отбрасыванием дробной части результата. (Поэтому 5 » 2 равно 1.)

Операция "поразрядное исключающее ИЛИ". Эта операция имеет очень интересные возможности. Она применима к целым операндам. Результат формируется при поразрядной обработке битовых кодов операндов. В тех разрядах, где оба операнда имеют одинаковые двоичные значения (1 и 1 или 0 и 0), резуль­ тат принимает значение 0. В тех разрядах, где биты операндов не совпадают, результат равен 1. Пример использования:

char

а='А';

/* внутренний код

01000001

*/

char

z='Z';

/* внутренний код

01011010

*/

a=aAz;

/* результат: 00011011

*/

 

z=aAz;

/* результат: 01000001

*/

 

a=aAz;

/* результат: 01011010

*/

 

Переменные а и z "обменялись" значениями без использова­ ния вспомогательной переменной!

Поразрядная дизъюнкция (поразрядное ИЛИ) применима к целочисленным операндам. В соответствии с назватгем она по­ зволяет получить 1 в тех разрядах результата, где не одновре­ менно равны 0 биты обоих операндов. Например:

5 [ 6 равно

7

(для 5 -ко д 101, для 6 -ко д 110);

10 | 8 равно

10

(для 10-к о д 1010, для 8 -код 1000).

Поразрядная конъюнкция (поразрядное И) применима к це­ лочисленным операндам. В битовом представлении результата только те биты равны 1, которым соответствуют единичные би­ ты обоих операндов. Примеры:

5&6 равно 4 (для 5 -ко д 101, для 6 - код 110); 10&8 равно 8 (для 10 - код 1010, для 8 - код 1000).

Условное выражение. Как уже говорилось в §1.4, операция, вводимая двумя лексемами '?' и':' (она имеет ранг 13), является уникальной. Во-первых, в нее входит не одна, а две лексемы, вовторых, она трехместная, т.е. должна иметь три операнда. С ее помощью формируется условное выражение, имеющее такой вид:

Соседние файлы в папке книги