Язык С

       

Преобразование типов


Если в выражениях встречаются операнды различных типов, то они преобразуются к общему типу в соответствии с неболь- шим набором правил. В общем, автоматически производятся только преобразования, имеющие смысл, такие как, например, преобразование целого в плавающее в выражениях типа F+I. Вы- ражения же, лишенные смысла, такие как использование пере- менной типа FLOAT в качестве индекса, запрещены. Во-первых, типы CHAR и INT могут свободно смешиваться в арифметических выражениях: каждая переменная типа CHAR авто- матически преобразуется в INT. Это обеспечивает значительную гибкость при проведении определенных преобразований симво- лов. Примером может служить функция ATOI, которая ставит в соответствие строке цифр ее численный эквивалент. ATOI(S) /* CONVERT S TO INTEGER */ CHAR S[]; { INT I, N;

N = 0; FOR ( I = 0; S[I]>='0' && S[I]<='9'; ++I) N = 10 * N + S[I] - '0'; RETURN(N); }

KAK Уже обсуждалось в главе 1, выражение

S[I] - '0'

имеет численное значение находящегося в S[I] символа, потому что значение символов '0', '1' и т.д. образуют возрастающую последовательность расположенных подряд целых положительных чисел. Другой пример преобразования CHAR в INT дает функция LOWER, преобразующая данную прописную букву в строчную. Если выступающий в качестве аргумента символ не является пропис- ной буквой, то LOWER возвращает его неизменным. Приводимая ниже программа справедлива только для набора символов ASCII.

LOWER(C) /* CONVERT C TO LOWER CASE; ASCII ONLY */ INT C; { IF ( C >= 'A' && C <= 'Z' ) RETURN( C + '@' - 'A'); ELSE /*@ Записано вместо 'A' строчного*/ RETURN(C); }

Эта функция правильно работает при коде ASCII, потому что численные значения, соответствующие в этом коде прописным и строчным буквам, отличаются на постоянную величину, а каждый алфавит является сплошным - между а и Z нет ничего, кроме букв. Это последнее замечание для набора символов EBCDIC систем IBM 360/370 оказывается несправедливым, в силу чего эта программа на таких системах работает неправильно - она преобразует не только буквы. При преобразовании символьных переменных в целые возни- кает один тонкий момент. Дело в том, что сам язык не указы- вает, должны ли переменным типа CHAR соответствовать числен- ные значения со знаком или без знака. Может ли при преобра- зовании CHAR в INT получиться отрицательное целое? К сожале- нию, ответ на этот вопрос меняется от машины к машине, отра- жая расхождения в их архитектуре. На некоторых машинах (PDP-11, например) переменная типа CHAR, крайний левый бит которой содержит 1, преобразуется в отрицательное целое ("знаковое расширение"). На других машинах такое преобразо- вание сопровождается добавлением нулей с левого края, в ре- зультате чего всегда получается положительное число.


Определение языка "C" гарантирует, что любой символ из стандартного набора символов машины никогда не даст отрица- тельного числа, так что эти символы можно свободно использо- вать в выражениях как положительные величины. Но произволь- ные комбинации двоичных знаков, хранящиеся как символьные переменные на некоторых машинах, могут дать отрицательные значения, а на других положительные. Наиболее типичным примером возникновения такой ситуации является сучай, когда значение 1 используется в качестве EOF. Рассмотрим программу

CHAR C; C = GETCHAR(); IF ( C == EOF ) ...

На машине, которая не осуществляет знакового расширения, переменная 'с' всегда положительна, поскольку она описана как CHAR, а так как EOF отрицательно, то условие никогда не выполняется. Чтобы избежать такой ситуации, мы всегда пре- дусмотрительно использовали 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.



- Затем, если один из операндов имеет тип 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). В этой конструкции, имеющей вид

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

SQRT((DOUBLE) N)

до передачи аргумента функции SQRT преобразует N к типу DOUBLE. (Отметим, что операция перевод преобразует значение N в надлежащий тип; фактическое содержание переменной N при этом не изменяется). Операция перевода имрация перевода име- ет тот же уровень старшинства, что и другие унарные опера- ции, как указывается в таблице в конце этой главы.

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

--------------- Составьте программу для функции HTOI(S), которая преоб- разует строку шестнадцатеричных цифр в эквивалентное ей це- лое значение. При этом допустимыми цифрами являются цифры от 1 до 9 и буквы от а до F.




    Содержание раздела