Керниган и Ритчи - Язык программирования Си (793773), страница 15
Текст из файла (страница 15)
В качестве примераприведем простенькую реализацию функции atoi, преобразующей последовательность цифр в ее числовойэквивалент./* atoi: преобразование s в целое */int atoi(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;}Как мы уже говорили в главе 1, выражениеs[i] - '0'дает числовое значение символа, хранящегося в s[i], так как значения '0' , '1' и пр. образуютнепрерывную возрастающую последовательность.Другой пример приведения char к int связан с функцией lower, которая одиночный символ из набораASCII, если он является заглавной буквой, превращает в строчную. Если же символ не является заглавнойбуквой, lower его не изменяет./* lower: преобразование с в строчную; только для ASCII */int lower(int с){if (с >= 'А' && с <= 'Z' )return с + 'а' - 'А';elsereturn с;}В случае ASCII эта программа будет работать правильно, потому что между одноименными буквами верхнегои нижнего регистров — одинаковое расстояние (если их рассматривать как числовые значения).
Кроме того,латинский алфавит — плотный, т. е. между буквами А и Z расположены только буквы. Для набора EBCDICпоследнее условие не выполняется, и поэтому наша программа в этом случае будет преобразовывать нетолько буквы.Стандартный заголовочный файл <ctype.h>, описанный в приложении B, определяет семейство функций,которые позволяют проверять и преобразовывать символы независимо от символьного набора. Например,функция tolower(c) возвращает букву c в коде нижнего регистра, если она была в коде верхнего регистра,поэтому tolower — универсальная замена функции lower, рассмотренной выше.
Аналогично проверкус >= '0' && с <= '9'можно заменить наisdigit(c)Далее мы будем пользоваться функциями из <ctype.h>.Существует одна тонкость, касающаяся преобразования символов в целые числа: язык не определяет,являются ли переменные типа char знаковыми или беззнаковыми. При преобразовании char в int можетли когда-нибудь получиться отрицательное целое? На машинах с разной архитектурой ответы могутотличаться. На некоторых машинах значение типа char с единичным старшим битом будет превращено вотрицательное целое (посредством "распространения знака").
На других преобразование char в intосуществляется добавлением нулей слева, и, таким образом, получаемое значение всегда положительно.Гарантируется, что любой символ из стандартного набора печатаемых символов никогда не будетотрицательным числом, поэтому в выражениях такие символы всегда являются положительнымиоперандами. Непроизвольный восьмибитовый код в переменной типа char на одних машинах может бытьотрицательным числом, а на других — положительным. Для совместимости переменные типа char, вкоторых хранятся несимвольные данные, следует специфицировать явно как signed или unsigned.Отношения вроде i > j и логические выражения, перемежаемые операторами && и ||, определяютвыражение-условие, которое имеет значение 1, если оно истинно, и 0, если ложно. Так, присваиваниеd = с >= '0' && с <= '9'установит d в значение 1, если с есть цифра, и 0 в противном случае.
Однако функции, подобные isdigit, вкачестве истины могут выдавать любое ненулевое значение. В местах проверок внутри if, while, for и пр."истина" просто означает "не нуль".Неявные арифметические преобразования, как правило, осуществляются естественным образом. В общемслучае, когда оператор вроде + или * с двумя операндами (бинарный оператор) имеет разнотипныеоперанды, прежде чем операция начнет выполняться, "низший" тип повышается до "высшего".
Результатбудет иметь высший тип. В параграфе 6 приложения А правила преобразования сформулированы точно. Еслиже в выражении нет беззнаковых операндов, можно удовлетвориться следующим набором неформальныхправил:Если какой-либо из операндов принадлежит типу long double, то и другой приводится к longdouble.В противном случае, если какой-либо из операндов принадлежит типу double, то и другойприводится к double.В противном случае, если какой-либо из операндов принадлежит типу float, то и другой приводитсяк float.В противном случае операнды типов char и short приводятся к int.И наконец, если один из операндов типа long, то и другой приводится к long.Заметим, что операнды типа float не приводятся автоматически к типу double; в этом данная версия языкаотличается от первоначальной.
Вообще говоря, математические функции, аналогичные собранным вбиблиотеке <math.h>, базируются на вычислениях с двойной точностью. В основном float используетсядля экономии памяти на больших массивах и не так часто — для ускорения счета на тех машинах, гдеарифметика с двойной точностью слишком дорога с точки зрения расхода времени и памяти.Правила преобразования усложняются с появлением операндов типа unsigned. Проблема в том, чтосравнения знаковых и беззнаковых значений зависят от размеров целочисленных типов, которые на разныхмашинах могут отличаться. Предположим, что значение типа int занимает 16 битов, а значение типа long —32 бита. Тогда -1L < 1U, поскольку 1U принадлежит типу unsigned int и повышается до типа signedlong.
Но -1L > 1UL, так как -1L повышается до типа unsigned long и воспринимается как большоеположительное число.Преобразования имеют место и при присвоениях: значение правой части присвоения приводится к типулевой части, который и является типом результата.Тип char превращается в int путем распространения знака или другим описанным выше способом.Тип long int преобразуются в short int или в значения типа char путем отбрасывания старшихразрядов. Так, вint i;char с;i = с;с = i;значение с не изменится.
Это справедливо независимо от того, распространяется знак при переводе char вint или нет. Однако, если изменить очередность присваиваний, возможна потеря информации.Если х принадлежит типу float, a i — типу int, то и х = i, и i = x вызовут преобразования, причемперевод float в int сопровождается отбрасыванием дробной части. Если double переводится во float,то значение либо округляется, либо обрезается; это зависит от реализации.Так как аргумент в вызове функции есть выражение, при передаче его функции также возможнопреобразование типа. При отсутствии прототипа функции аргументы типа char и short переводятся в int,a float — в double.
Вот почему мы объявляли аргументы типа int или double даже тогда, когда в вызовефункции использовали аргументы типа char или float.И наконец, для любого выражения можно явно ("насильно") указать преобразование его типа, используяунарный оператор, называемый приведением. Конструкция вида(имя-типа) выражениеприводит выражение к указанному в скобках типу по перечисленным выше правилам. Смысл операцииприведения можно представить себе так: выражение как бы присваивается некоторой переменнойуказанного типа, и эта переменная используется вместо всей конструкции. Например, библиотечная функцияsqrt рассчитана на аргумент типа double и выдает чепуху, если ей подсунуть что-нибудь другое (sqrtописана в <math.h>). Поэтому, если n имеет целочисленный тип, мы можем написатьsqrt((double) n)и перед тем, как значение n будет передано функции, оно будет переведено в double.
Заметим, чтооперация приведения всего лишь вырабатывает значение n указанного типа, но саму переменную n незатрагивает. Приоритет оператора приведения столь же высок, как и любого унарного оператора, чтозафиксировано в таблице, помещенной в конце этой главы.В том случае, когда аргументы описаны в прототипе функции, как тому и следует быть, при вызове функциинужное преобразование выполняется автоматически. Так, при наличии прототипа функции sqrt:double sqrt(double);перед обращением к sqrt в присваиванииroot2 = sqrt(2);целое 2 будет переведено в значение doubleприведения.2.0 автоматически без явного указания операцииОперацию приведения проиллюстрируем на переносимой версии генератора псевдослучайных чисел ифункции, инициализирующей "семя".
И генератор, и функция входят в стандартную библиотеку.unsigned long int next = 1;/* rand: возвращает псевдослучайное целое 0...32767 */int rand(void){next = next * 1103515245 + 12345;return (unsigned int)(next/65536) % 32768;}/* srand: устанавливает "семя" для rand() */void srand(unsigned int seed){next = seed;}Упражнение 2.З.
Напишите функцию htol(s), которая преобразует последовательностьшестнадцатеричных цифр, начинающуюся с 0х или 0Х, в соответствующее целое. Шестнадцатеричнымицифрами являются символы 0…9, а…f, А…F.2.8. Операторы инкремента и декрементаВ Си есть два необычных оператора, предназначенных для увеличения и уменьшения переменных. Операторинкремента ++ добавляет 1 к своему операнду, а оператор декремента -- вычитает 1. Мы уже неоднократноиспользовали ++ для наращивания значения переменных, как, например, вif (с == '\n')++nl;Необычность операторов ++ и -- в том, что их можно использовать и как префиксные (помещая передпеременной: ++n), и как постфиксные (помещая после переменной: n++) операторы. В обоих случаяхзначение n увеличивается на 1, но выражение ++n увеличивает n до того, как его значение будетиспользовано, а n++ — после того.














