Смекни!
smekni.com

дусмотрительно использовали INT вместо CHAR для любой пере-

менной, получающей значение от GETCHAR.

Основная же причина использования INT вместо CHAR не

связана с каким-либо вопросом о возможном знаковом расшире-

нии. просто функция GETCHAR должна передавать все возможные

символы (чтобы ее можно было использовать для произвольного

ввода) и, кроме того, отличающееся значение EOF. Следова-

тельно значение EOF не может быть представлено как CHAR, а

должно храниться как INT.

Другой полезной формой автоматического преобразования

типов является то, что выражения отношения, подобные I>J, и

логические выражения, связанные операциями && и \!\!, по оп-

ределению имеют значение 1, если они истинны, и 0, если они

ложны. Таким образом, присваивание

ISDIGIT = C >= '0' && C <= '9';

полагает ISDIGIT равным 1, если с - цифра, и равным 0 в про-

тивном случае. (В проверочной части операторов IF, WHILE,

FOR и т.д. “Истинно” просто означает “не нуль”).

Неявные арифметические преобразования работают в основ-

ном, как и ожидается. В общих чертах, если операция типа +

или *, которая связывает два операнда (бинарная операция),

имеет операнды разных типов, то перед выполнением операции

“низший” тип преобразуется к “высшему” и получается резуль-

тат “высшего” типа. Более точно, к каждой арифметической

операции применяется следующая последовательность правил

преобразования.

· Типы CHAR и SHORT преобразуются в INT, а FLOAT в

DOUBLE.

·
48 -

· Затем, если один из операндов имеет тип DOUBLE, то

другой преобразуется в DOUBLE, и результат имеет тип DOUBLE.

· В противном случае, если один из операндов имеет тип LONG, то другой преобразуется в LONG, и результат имеет тип LONG.

· В противном случае, если один из операндов имеет тип UNSIGNED, то другой преобразуется в UNSIGNED и результат имеет тип UNSIGNED.

· В противном случае операнды должны быть типа INT, и

результат имеет тип INT.

Подчеркнем, что все переменные типа FLOAT в выражениях пре-

образуются в DOUBLE; в “C” вся плавающая арифметика выполня-

ется с двойной точностью.

Преобразования возникают и при присваиваниях; значение

правой части преобразуется к типу левой, который и является

типом результата. Символьные переменные преобразуются в це-

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

выше. Обратное преобразование INT в CHAR ведет себя хорошо -

лишние биты высокого порядка просто отбрасываются. Таким об-

разом

INT I;

CHAR C;

I = C;

C = I;

значение 'с' не изменяется. Это верно независимо от того,

вовлекается ли знаковое расширение или нет.

Если х типа FLOAT, а I типа INT, то как

х = I;

так и

I = х;

приводят к преобразованиям; при этом FLOAT преобразуется в

INT отбрасыванием дробной части. Тип DOUBLE преобразуется во

FLOAT округлением. Длинные целые преобразуются в более ко-

роткие целые и в переменные типа CHAR посредством отбрасыва-

ния лишних битов высокого порядка.

Так как аргумент функции является выражением, то при пе-

редаче функциям аргументов также происходит преобразование

типов: в частности, CHAR и SHORT становятся INT, а FLOAT

становится DOUBLE. Именно поэтому мы описывали аргументы

функций как INT и DOUBLE даже тогда, когда обращались к ним

с переменными типа CHAR и FLOAT.

Наконец, в любом выражении может быть осуществлено

(“принуждено”) явное преобразование типа с помощью конструк-

ции, называемой перевод (CAST). В этой конструкции, имеющей

вид

(имя типа) выражение

· 49 -

Выражение преобразуется к указанному типу по правилам

преобразования, изложенным выше. Фактически точный смысл

операции перевода можно описать следующим образом: выражение

как бы присваивается некоторой переменной указанного типа,

которая затем используется вместо всей конструкции. Напри-

мер, библиотечная процедура SQRT ожидает аргумента типа

DOUBLE и выдаст бессмысленный ответ, если к ней по небреж-

ности обратятся с чем-нибудь иным. таким образом, если N -

целое, то выражение

SQRT((DOUBLE) N)

до передачи аргумента функции SQRT преобразует N к типу

DOUBLE. (Отметим, что операция перевод преобразует значение

N в надлежащий тип; фактическое содержание переменной N при

этом не изменяется). Операция перевода имрация перевода име-

ет тот же уровень старшинства, что и другие унарные опера-

ции, как указывается в таблице в конце этой главы.

Упражнение 2-2.

Составьте программу для функции HTOI(S), которая преоб-

разует строку шестнадцатеричных цифр в эквивалентное ей це-

лое значение. При этом допустимыми цифрами являются цифры от

1 до 9 и буквы от а до F.

2.8. Операции увеличения и уменьшения

В языке “C” предусмотрены две необычные операции для

увеличения и уменьшения значений переменных. Операция увели-

чения ++ добавляет 1 к своему операнду, а операция уменьше-

ния—вычитает 1. Мы часто использовали операцию ++ для

увеличения переменных, как, например, в

IF(C == '&bsol;N')

++I;

Необычный аспект заключается в том, что ++ и—можно

использовать либо как префиксные операции (перед переменной,

как в ++N), либо как постфиксные (после переменной: N++).

Эффект в обоих случаях состоит в увеличении N. Но выражение

++N увеличивает переменную N до использования ее значения, в

то время как N++ увеличивает переменную N после того, как ее

значение было использовано. Это означает, что в контексте,

где используется значение переменной, а не только эффект

увеличения, использование ++N и N++ приводит к разным ре-

зультатам. Если N = 5, то

х = N++;

устанавливает х равным 5, а

х = ++N;

·
50 -

полагает х равным 6. В обоих случаях N становится равным 6.

Операции увеличения и уменьшения можно применять только к

переменным; выражения типа х=(I+J)++ являются незаконными.

В случаях, где нужен только эффект увеличения, а само

значение не используется, как, например, в

IF ( C == '&bsol;N' )

NL++;

выбор префиксной или постфиксной операции является делом

вкуса. но встречаются ситуации, где нужно использовать имен-

но ту или другую операцию. Рассмотрим, например, функцию

SQUEEZE(S,C), которая удаляет символ 'с' из строки S, каждый

раз, как он встречается.

SQUEEZE(S,C) /* DELETE ALL C FROM S */

CHAR S[];

INT C;

{

INT I, J;

FOR ( I = J = 0; S[I] != '&bsol;0'; I++)

IF ( S[I] != C )

S[J++] = S[I];

S[J] = '&bsol;0';

}

Каждый раз, как встечается символ, отличный от 'с', он копи-

руется в текущую позицию J, и только после этого J увеличи-

вается на 1, чтобы быть готовым для поступления следующего

символа. Это в точности эквивалентно записи

IF ( S[I] != C ) {

S[J] = S[I];

J++;

}

Другой пример подобной конструкции дает функция GETLINE,

которую мы запрограммировали в главе 1, где можно заменить

IF ( C == '&bsol;N' ) {

S[I] = C;

++I;

}

более компактной записью

IF ( C == '&bsol;N' )

S[I++] = C;

· 51 -

В качестве третьего примера рассмотрим функцию

STRCAT(S,T), которая приписывает строку т в конец строки S,

образуя конкатенацию строк S и т. При этом предполагается,

что в S достаточно места для хранения полученной комбинации.

STRCAT(S,T) /* CONCATENATE T TO END OF S */

CHAR S[], T[]; /* S MUST BE BIG ENOUGH */

{

INT I, J;

I = J = 0;

WHILE (S[I] != '&bsol;0') / *FIND END OF S */

I++;

WHILE((S[I++] = T[J++]) != '&bsol;0') /*COPY T*/

;

}

Tак как из T в S копируется каждый символ, то для подготовки

к следующему прохождению цикла постфиксная операция ++ при-

меняется к обеим переменным I и J.

Упражнение 2-3.

Напишите другой вариант функции SQUEEZE(S1,S2), который

удаляет из строки S1 каждый символ, совпадающий с каким-либо

символом строки S2.

Упражнение 2-4.

Напишите программу для функции ANY(S1,S2), которая нахо-

дит место первого появления в строке S1 какого-либо символа

из строки S2 и, если строка S1 не содержит символов строки

S2, возвращает значение -1.

2.9. Побитовые логические операции

В языке предусмотрен ряд операций для работы с битами;

эти операции нельзя применять к переменным типа FLOAT или

DOUBLE.

& Побитовое AND

&bsol;! Побитовое включающее OR

^ побитовое исключающее OR

<< сдвиг влево

>> сдвиг вправо

&bsol;^ дополнение (унарная операция)

“&bsol;” иммитирует вертикальную черту.

Побитовая операция AND часто используется для маскирования

некоторого множества битов; например, оператор

C = N & 0177

· 52 -

передает в 'с' семь младших битов N , полагая остальные рав-

ными нулю. Операция 'э' побитового OR используется для вклю-

чения битов:

C = X э MASK

устанавливает на единицу те биты в х , которые равны единице

в MASK.

Следует быть внимательным и отличать побитовые операции

& и 'э' от логических связок && и &bsol;!&bsol;! , Которые подразуме-

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

если х=1, а Y=2, то значение х&Y равно нулю , в то время как

значение X&&Y равно единице./почему?/

Операции сдвига << и >> осуществляют соответственно

сдвиг влево и вправо своего левого операнда на число битовых

позиций, задаваемых правым операндом. Таким образом , х<<2

сдвигает х влево на две позиции, заполняя освобождающиеся

биты нулями, что эквивалентно умножению на 4. Сдвиг вправо

величины без знака заполняет освобождающиеся биты на некото-

рых машинах, таких как PDP-11, заполняются содержанием зна-

кового бита /”арифметический сдвиг”/, а на других - нулем

/”логический сдвиг”/.

Унарная операция &bsol;^ дает дополнение к целому; это озна-

чает , что каждый бит со значением 1 получает значение 0 и

наоборот. Эта операция обычно оказывается полезной в выраже-

ниях типа

X & &bsol;^077

где последние шесть битов х маскируются нулем. Подчеркнем,

что выражение X&&bsol;^077 не зависит от длины слова и поэтому

предпочтительнее, чем, например, X&0177700, где предполага-

ется, что х занимает 16 битов. Такая переносимая форма не

требует никаких дополнительных затрат, поскольку &bsol;^077 явля-

ется константным выражением и, следовательно, обрабатывается