
книги / Программирование на языке Си
..pdf52 Программирование на языке Си
х+++Ь эквивалентно (х++)+Ь 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), является уникальной. Во-первых, в нее входит не одна, а две лексемы, вовторых, она трехместная, т.е. должна иметь три операнда. С ее помощью формируется условное выражение, имеющее такой вид: