RuLibrary.com

ГЛАВНАЯ | ПОИСК | ТОП | КАРТА САЙТА      

 
 


 

Керниган Ричи >> Язык C (страница 3)


Мы также писали 32.0 вместо 32 , несмотря на то, что так как переменная FAHR имеет тип FLOAT , целое 32 автоматически бы преобразовалось к типу FLOAT ( в 32.0) перед вычитанием. С точки зрения стиля разумно писать плавающие константы с явной десятичной точкой даже тогда, когда они имеют целые значения; это подчеркивает их плавающую природу для просмат- ривающего программу и обеспечивает то, что компилятор будет смотреть на вещи так же, как и Вы.

Подробные правила о том, в каком случае целые преобразу- ются к типу с плаваюшей точкой, приведены в главе 2. Сейчас же отметим, что присваивание

FAHR = LOWER;

проверка

WHILE (FAHR <= UPPER)

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

Этот же пример сообщает чуть больше о том, как работает PRINTF. Функция PRINTF фактически является универсальной функцией форматных преобразований, которая будет полностью описана в главе 7. Ее первым аргументом является строка сим- волов, которая должна быть напечатана, причем каждый знак % указывает, куда должен подставляться каждый из остальных ар- гументов /второй, третий, .../ и в какой форме он должен пе- чататься. Например, в операторе

PRINTF("%4.0F %6.1F\N", FAHR, CELSIUS);

спецификация преобразования %4.0F говорит, что число с пла- вающей точкой должно быть напечатано в поле шириной по край- ней мере в четыре символа без цифр после десятичной точки. спецификация %6.1F описывает другое число, которое должно занимать по крайней мере шесть позиций с одной цифрой после десятичной точки, аналогично спецификациям F6.1 в фортране или F(6,1) в PL/1. Различные части спецификации могут быть опущены: спецификация %6F говорит, что число будет шириной по крайней мере в шесть символов; спецификация %2 требует двух позиций после десятичной точки, но ширина при этом не ограничивается; спецификация %F говорит только о том, что нужно напечатать число с плавающей точкой. Функция PRINTF также распознает следующие спецификации: %D - для десятично- го целого, %о - для восьмеричного числа, %х - для шестнадца- тиричного, %с - для символа, %S - для символьной строки и %% - для самого символа %.

Каждая конструкция с символом % в первом аргументе функ- ции PRINTF сочетается с соответствующим вторым, третьим, и т.д. Аргументами; они должны согласовываться по числу и ти- пу; в противном случае вы получите бессмысленные результаты.

Между прочим, функция PRINTF не является частью языка "C"; в самом языке "C" не определены операции ввода-вывода. Нет ничего таинственного и в функции PRINTF ; это - просто полезная функция, являющаяся частью стандартной библиотеки подпрограмм, которая обычно доступна "C"-программам. Чтобы сосредоточиться на самом языке, мы не будем подробно оста- навливаться на операциях ввода-вывода до главы 7. В частнос- ти, мы до тех пор отложим форматный ввод. Если вам надо ввести числа - прочитайте описание функции SCANF в главе 7, раздел 7.4. Функция SCANF во многом сходна с PRINTF , но она считывает входные данные, а не печатает выходные.

Упражнение 1-3

Преобразуйте программу перевода температур таким обра- зом, чтобы она печатала заголовок к таблице.

Упражнение 1-4

Напишите программы печати соответствующей таблицы пере- хода от градусов цельсия к градусам фаренгейта.

1.3. Оператор FOR

Как и можно было ожидать, имеется множество различных способов написания каждой программы. Давайте рассмотрим та- кой вариант программы перевода температур:

MAIN() /* FAHRENHEIT-CELSIUS TABLE */ {

INT FAHR;

FOR (FAHR = 0; FAHR <= 300; FAHR = FAHR + 20)

PRINTF("%4D %6.1F\N", FAHR, (5.0/9.0)*(FAHR-32.0)); }

Эта программа выдает те же самые результаты, но выглядит безусловно по-другому. Главное изменение - исключение боль- шинства переменных; осталась только переменная FAHR , причем типа INT (это сделано для того, чтобы продемонстрировать преобразование %D в функции PRINTF). Нижняя и верхняя грани- цы и размер щага появляются только как константы в операторе FOR , который сам является новой конструкцией, а выражение, вычисляющее температуру по цельсию, входит теперь в виде третьего аргумента функции PRINTF , а не в виде отдельного оператора присваивания.

Последнее изменение является примером вполне общего пра- вила языка "C" - в любом контексте, в котором допускается использование значения переменной некоторого типа, вы можете использовать выражение этого типа. Так как третий аргумент функции PRINTF должен иметь значение с плавающей точкой, чтобы соответствовать спецификации %6.1F, то в этом месте может встретиться любое выражение плавающего типа.

Сам оператор FOR - это оператор цикла, обобщающий опера- тор WHILE. Его функционирование должно стать ясным, если вы сравните его с ранее описанным оператором WHILE . Оператор FOR содержит три части, разделяемые точкой с запятой. Первая часть

FAHR = 0

выполняется один раз перед входом в сам цикл. Вторая часть - проверка, или условие, которое управляет циклом:

FAHR <= 300

это условие проверяется и, если оно истинно, то выполняется тело цикла (в данном случае только функция PRINTF ). Затем выполняется шаг реинициализации FAHR =FAHR + 20

и условие проверяется снова. цикл завершается, когда это ус- ловие становится ложным. Так же, как и в случае оператора WHILE , тело цикла может состоять из одного оператора или из группы операторов, заключенных в фигурные скобки. Инициали- зирующая и реинициализирующая части могут быть любыми от- дельными выражениями.

Выбор между операторами WHILE и FOR произволен и основы- вается на том , что выглядит яснее. Оператор FOR обычно удо- бен для циклов, в которых инициализация и реинициализация логически связаны и каждая задается одним оператором, так как в этом случае запись более компактна, чем при использо- вании оператора WHILE , а операторы управления циклом сосре- дотачиваются вместе в одном месте.

Упражнение 1-5

Модифицируйте программу перевода температур таким обра- зом, чтобы она печатала таблицу в обратном порядке, т.е. От 300 градусов до 0.

1.4. Символические константы

Последнее замечание, прежде чем мы навсегда оставим программу перевода температур. Прятать "магические числа", такие как 300 и 20, внутрь программы - это неудачная практи- ка; они дают мало информации тем, кто, возможно, должен бу- дет разбираться в этой программе позднее, и их трудно изме- нять систематическим образом. К счастью в языке "C" предус- мотрен способ, позволяющий избежать таких "магических чи- сел". Используя конструкцию #DEFINE , вы можете в начале программы определить символическое имя или символическую константу, которая будет конкретной строкой символов. Впос- ледствии компилятор заменит все не заключенные в кавычки по- явления этого имени на соответствующую строку. Фактически это имя может быть заменено абсолютно произвольным текстом, не обязательно цифрами

#DEFINE LOWER 0/* LOWER LIMIT OF TABLE */

#DEFINE UPPER 300 /* UPPER LIMIT */

#DEFINE STEP 20 /* STEP SIZE */

MAIN () /* FAHRENHEIT-CELSIUS TABLE */

{

INT FAHR; FOR (FAHR =LOWER; FAHR <= UPPER; FAHR =FAHR + STEP)

PRINTF("%4D %6.1F\N", FAHR, (5.0/9.0)*(FAHR-32));

}

величины LOWER, UPPER и STEP являются константами и поэ- тому они не указываются в описаниях. Символические имена обычно пишут прописными буквами, чтобы их было легко отли- чить от написанных строчными буквами имен переменных. отме- тим, что в конце определения не ставится точка с запятой. Так как подставляется вся строка, следующая за определенным именем, то это привело бы к слишком большому числу точек с запятой в операторе FOR .

1.5. Набор полезных программ

Теперь мы собираемся рассмотреть семейство родственных программ, предназначенных для выполнения простых операций над символьными данными. В дальнейшем вы обнаружите, что многие программы являются просто расширенными версиями тех прототипов, которые мы здесь обсуждаем.

1.5.1. Ввод и вывод символов

Стандартная библиотека включает функции для чтения и за- писи по одному символу за один раз. функция GETCHAR() извле- кает следующий вводимый символ каждый раз, как к ней обраща- ются, и возвращает этот символ в качестве своего значения. Это значит, что после

C = GETCHAR()

переменная 'C' содержит следующий символ из входных данных. Символы обычно поступают с терминала, но это не должно нас касаться до главы 7.

Функция PUTCHAR(C) является дополнением к GETCHAR : в результате обращения

PUTCHAR (C)

содержимое переменной 'C' выдается на некоторый выходной но- ситель, обычно опять на терминал. Обращение к функциям PUTCHAR и PRINTF могут перемежаться; выдача будет появляться в том порядке, в котором происходят обращения.

Как и функция PRINTF , функции GETCHAR и PUTCHAR не со- держат ничего экстраординарного. Они не входят в состав язы- ка "C", но к ним всегда можно обратиться.

1.5.2. Копирование файла

Имея в своем распоряжении только функции GETCHAR и PUTCHAR вы можете, не зная ничего более об операциях вво- да-вывода, написать удивительное количество полезных прог- рамм. Простейшим примером может служить программа посимволь- ного копирования вводного файла в выводной. Общая схема име- ет вид: ввести символ WHILE (символ не является признаком конца файла)

вывести только что прочитанный символ

ввести новый символ

программа, написанная на языке "C", выглядит следующим обра- зом:

MAIN() /* COPY INPUT TO OUTPUT; 1ST VERSION */

{

INT C;

C = GETCHAR();

WHILE (C != EOF) {

PUTCHAR (C);

C = GETCHAR();

}

}

оператор отношения != означает "не равно".

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

#DEFINE EOF -1

либо

#DEFINE EOF 0

Использовав символическую константу EOF для представле- ния значения, возвращаемого функцией GETCHAR при выходе на конец файла, мы обеспечили, что только одна величина в прог- рамме зависит от конкретного численного значения.

Мы также описали переменную 'C' как INT , а не CHAR , с тем чтобы она могла хранить значение, возвращаемое GETCHAR . как мы увидим в главе 2, эта величина действительно INT, так как она должна быть в состоянии в дополнение ко всем возмож- ным символам представлять и EOF.

Программистом, имеющим опыт работы на "C", программа копирования была бы написана более сжато. В языке "C" любое присваивание, такое как

C = GETCHAR()

может быть использовано в выражении; его значение - просто значение, присваиваемое левой части. Если присваивание сим- вола переменной 'C' поместить внутрь проверочной части опе- ратора WHILE , то программа копирования файла запишется в виде:

MAIN() /* COPY INPUT TO OUTPUT; 2ND VERSION */ { INT C;

WHILE ((C = GETCHAR()) != EOF) PUTCHAR(C); }

Программа извлекает символ , присваивает его переменной 'C' и затем проверяет, не является ли этот символ признаком конца файла. Если нет - выполняется тело оператора WHILE, выводящее этот символ. Затем цикл WHILE повторяется. когда, наконец, будет достигнут конец файла ввода, оператор WHILE завершается, а вместе с ним заканчивается выполнение и функ- ции MAIN .

В этой версии централизуется ввод - в программе только одно обращение к функции GETCHAR - и ужимается программа. Вложение присваивания в проверяемое условие - это одно из тех мест языка "C", которое приводит к значительному сокра- щению программ. Однако, на этом пути можно увлечься и начать писать недоступные для понимания программы. Эту тенденцию мы будем пытаться сдерживать.

Важно понять , что круглые скобки вокруг присваивания в условном выражении действительно необходимы. Старшинство операции != выше, чем операции присваивания =, а это означа- ет, что в отсутствие круглых скобок проверка условия != бу- дет выполнена до присваивания =. Таким образом, оператор

C = GETCHAR() != EOF

эквивалентен оператору

C = (GETCHAR() != EOF)

Это, вопреки нашему желанию, приведет к тому, что 'C' будет принимать значение 0 или 1 в зависимости от того, на- толкнется или нет GETCHAR на признак конца файла. Подробнее об этом будет сказано в главе 2/.

1.5.3. Подсчет символов

Следующая программа подсчитывает число символов; она представляет собой небольшое развитие программы копирования.

MAIN() /* COUNT CHARACTERS IN INPUT */

{

LONG NC;

NC = 0;

WHILE (GETCHAR() != EOF )

++NC;

PRINTF("%1D\N", NC);

}

Оператор

++NC;

демонстрирует новую операцию, ++, которая означает увеличе- ние на единицу. Вы могли бы написать NC = NC + 1 , но ++NC более кратко и зачастую более эффективно. Имеется соответст- вующая операция -- уменьшение на единицу. Операции ++ и -- могут быть либо префиксными (++NC), либо постфиксными (NC++); эти две формы, как будет показано в главе 2, имеют в выражениях различные значения, но как ++NC, так и NC++ уве- личивают NC. Пока мы будем придерживаться префиксных опера- ций.

Программа подсчета символов накапливает их количество в переменной типа LONG, а не INT . На PDP-11 максимальное зна- чение равно 32767, и если описать счетчик как INT , то он будет переполняться даже при сравнительно малом файле ввода; на языке "C" для HONEYWELL и IBM типы LONG и INT являются синонимами и имеют значительно больший размер. Спецификация преобразования %1D указывает PRINTF , что соответствующий аргумент является целым типа LONG .

Название книги: Язык C
Автор: Керниган Ричи
Просмотрено 45088 раз

123456789101112131415161718192021222324252627282930313233


 
Page generation 0.003 seconds
Пора отдыхать по тайским обычаям в отпуск ланта тур тайланд весна 2010.