Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

MM2 TP

.pdf
Скачиваний:
9
Добавлен:
09.02.2015
Размер:
3.63 Mб
Скачать

2.3Переменные, арифметика, цикл WHILE.

Возврат_к_оглавлению

Дальнейшее изучение основных элементов языка C продолжим на примере програм-

мы, которая распечатывает таблицу соответствия температур по Фарентгейту и Цельсию.

/* EX0002.C РАСПЕЧАТКА ТАБЛИЦЫ ФАРЕНТГЕЙТ - ЦЕЛЬСИЙ ДЛЯ F = 0, 20, ..., 300 */

int main() {

int lower, upper, step; float fahr, celsius;

lower = 0; /* НИЖНЯЯ ГРАНИЦА ТАБЛИЦЫ ТЕМПЕРАТУРЫ */ upper = 300; /* ВЕРХНЯЯ ГРАНИЦА */

step = 20; /* ВЕЛИЧИНА ШАГА */ fahr = lower;

while (fahr <= upper) {

celsius = (5.0/9.0) * (fahr -32.0); printf("(%4.0f) [%6.1f]\n", fahr, celsius); fahr = fahr + step;

}

return(0);

}

Оператор присваивания: FAHR = LOWER;

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

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

менная UPPER преобразуется к плавающему типу.

Выражение для расчета величины температуры по Цельсию (CELSIUS) так же имеет особенности.

Константы 5.0 и 9.0 записаны с десятичными точками для того, чтобы компьютер рас-

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

Наличие или отсутствие десятичной точки в константе 32.0 в данном случае с вычис-

лительной точки зрения значения не имеет, но наличие точки облегчает понимание про-

граммы.

Рассматриваемая программа показывает, что функция PRINTF имеет более сложную структуру, чем это было показано в прошлом примере. В данном случае PRINTF имеет 3

параметра, разделенные запятой. Первый параметр называется строкой формата. Основ-

ной задачей PRINTF является отображение на экране строки формата. Символы, входя-

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

бражаются на экране непосредственно. Появление служебных символов подразумевает выполнение некоторых действий. Ранее уже встречалась служебная комбинация \n. Эти символы не отображаются, они служат командой формирования новой строки. В текущей программе встречаются служебные комбинации, начинающиеся со знака %. Наличие та-

кой комбинации в строке формата подразумевает наличие соответствующего параметра в

PRINTF. Соответствие устанавливается по порядку появления комбинации в строке фор-

мата и параметра в списке параметров PRINTF. В данном случае выполняется следующее соответствие:

%4.0F

FAHR

%6.1F

CELSIUS

В данном случае при обнаружении служебной комбинации %4.0F на экран выводится значение переменной FAHR как числа с плавающей точкой, занимающее не менее 4 зна-

комест без цифр после десятичной точки; при обнаружении комбинации %6.1F выводится значение переменной CELSIUS, занимающее не менее 6 знакомест с одной цифрой после десятичной точки (позиция, занимаемая десятичной точкой входит в число 6).

Рис. 18. Исходный модуль EX0002.C и первые 5 строк распечатки результата работы программы.

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

Выполнить эксперименты со строкой формата PRINTF.

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

Модернизировать EX0002.C так, чтобы таблица температур содержала заголовок.

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

Написать программу обратного преобразования(Цельсий Фарентгейт).

2.4Цикл FOR, символические константы.

Возврат_к_оглавлению

Для иллюстрации новых конструкций языка C рассмотрим исходный модуль

EX0003.C, в котором реализован прежний алгоритм формирования таблицы Фарентгейт – Цельсий.

/* EX0003.C */

int main() /* ТАБЛИЦА ФАРЕНТГЕЙТ ЦЕЛЬСИЙ */

{

int fahr;

for (fahr = 0; fahr <= 300; fahr = fahr + 20) printf("%4d %6.1f\n", fahr, (5.0/9.0)*(fahr-32));

return(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, а операторы управления циклом сосредотачиваются в одном месте.

Упражнение 5.

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

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

менять систематическим образом. К счастью в языке C предусмотрен способ, позволяю-

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

ные в кавычки появления этого имени на соответствующую строку. Фактически это имя может быть заменено абсолютно произвольным текстом, не обязательно цифрами

/* EX0004.C */

#define LOWER 0 /* НИЖНЯЯ ГРАНИЦА ТАБЛИЦЫ */ #define UPPER 300 /* ВЕРХНЯЯ ГРАНИЦА */

#define STEP 20 /* ВЕЛИЧИНА ШАГА */ int main() {

int fahr;

for (fahr =LOWER; fahr <= UPPER; fahr = fahr + STEP) printf("%4d %6.1f\n", fahr, (5.0/9.0) * (fahr - 32));

return(0);

}

Величины LOWER, UPPER и STEP являются константами и поэтому они не указыва-

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

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

2.5Ввод и вывод символов

Возврат_к_оглавлению

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

ченных для выполнения простых операций над символьными данными, т.е. элементами текста. При отсутствии специальных установок рассматриваемые функции рассматривают в качестве устройства ввода – клавиатуру, а в качестве устройства вывода – экран.

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

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

PUTCHAR (C) содержимое переменной C выдается на выходное устройство.

Рассмотрим использование функций на примере программы, посимвольного копиро-

вания ввода с клавиатуры на экран.

/* EX0005.C */ #include <stdio.h>

int main() /* копирование символов */

{

int c;

c = getchar(); while (c != EOF) {

putchar (c); c = getchar();

}

return(0);

}

Новым элементом языка в приведенной программе является оператор != , который оз-

начает "не равно".

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

она возвращает значение, не являющееся действительным символом; таким образом, про-

грамма, вызывающая GETCHAR() может установить, что ввод исчерпан. Общеупотреби-

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

мый GETCHAR() является числом (не символом текста). По этой причине для признака конца в программе используется символическое имя EOF, которое определяется в стан-

дартном файле заголовков. Директива #INCLUDE <STDIO.H> сообщает компилятору, что перед преобразованием текста исходного модуля в машинный код, необходимо использо-

вать информацию, находящуюся в файле STDIO.H.

В программе копирования переменная C описана как INT , а не CHAR , с тем чтобы она могла хранить все значения, возвращаемое GETCHAR, в том числе и EOF.

Поведение программ существенно зависит от принятого в компьютере способа орга-

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

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

Для учета вышеописанного функция GETCHAR() передает информацию в вызываю-

щую программу в два этапа.

На первом этапе GETCHAR() посылает операционной системе запрос на заполнение символами, вводимыми с клавиатуры, специальной области памяти – буфера. После пере-

дачи запроса GETCHAR() переходит в режим ожидания заполнения буфера. В случае вво-

да данных с клавиатуры буфер считается заполненным, когда оператор введет признак конца строки (ENTER) или конца ввода (CTRL+Z).

На втором этапе GETCHAR() возвращает в вызывающую программу очередной сим-

вол из буфера. Один символ за одно обращение к GETCHAR(). Признаки конца сроки и конца ввода также передаются в вызывающую программу.

При исчерпании буфера GETCHAR() переходит к первому этапу работы.

Все вышеописанное объясняет почему при работе рассматриваемой программы вывод появляется на экране только после ввода оператором признаков конца строки или конца ввода.

Ввод, выполненный пользователем и завершенный ENTER

Вывод программы.

Ввод пользователя: 123<CTRL+Z><ENTER>

Рис. 19. Иллюстрация работы программы копирования символов EX0005.C.

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

вание, такое как C= GETCHAR() может быть использовано в выражении; его значение -

просто значение, присваиваемое левой части. Если присваивание символа переменной C

поместить внутрь проверочной части оператора WHILE , то программа копирования фай-

ла запишется в виде:

/* EX0006.C */ #include <stdio.h>

int main() { /* копирование символов; версия 2 */ int c;

while ((c = getchar()) != EOF) putchar(c);

return (0);

}

Программа извлекает символ, присваивает его переменной C и затем проверяет, не яв-

ляется ли этот символ признаком конца. Если нет - выполняется тело оператора WHILE,

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

В этой версии централизуется ввод - в программе только одно обращение к функции

GETCHAR - и ужимается программа. Вложение присваивания в проверяемое условие -

это одно из тех мест языка C, которое приводит к значительному сокращению текста про-

грамм. Однако, на этом пути можно увлечься и начать писать недоступные для понимания программы.

Важно понять, что круглые скобки вокруг присваивания в условном выражении дейст-

вительно необходимы. Старшинство операции != выше, чем операции присваивания =, а

это означает, что в отсутствие круглых скобок проверка условия != будет выполнена до присваивания =. Таким образом, оператор

C = GETCHAR() != EOF

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

C = (GETCHAR() != EOF)

Это, вопреки нашему желанию, приведет к тому, что C будет принимать значение не от функции GETCHAR(), а от константы EOF.

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

большое развитие программы копирования.

/* EX0007.C */ #include <stdio.h>

int main() { /* подсчет символов */ long nc;

nc = 0;

while (getchar() != EOF) ++nc;

printf("%1d\n", nc); return(0);

}

Оператор ++NC демонстрирует новую операцию, ++, которая означает увеличение на единицу. Вы могли бы написать NC = NC + 1, но ++NC более кратко и зачастую более эффективно, т.к. во многих машинах имеются специальные операции – уменьше-

ние/увеличение на единицу. Операции ++ и -- могут быть либо префиксными (++NC), ли-

бо постфиксными (NC++); эти две формы, как будет показано далее, имеют в выражениях различные значения, но как ++NC, так и NC++ увеличивают NC.

Программа подсчета символов накапливает их количество в переменной типа LONG, а

не INT. Максимальное значение, которое можно хранить в переменной типа INT равно

32767, и если описать счетчик как INT , то он будет переполняться даже при сравнительно малом файле ввода. Спецификация преобразования %1D указывает PRINTF , что соответ-

ствующий аргумент является целым типа LONG .

Чтобы справиться с еще большими числами, можно использовать тип DOUBLE / FLOAT двойной длины/.

В новой программе будет использован оператор FOR вместо WHILE с тем, чтобы про-

иллюстрировать другой способ записи цикла.

/* EX0008.C */ #include <stdio.h> int main() {

double nc;

for (nc = 0; getchar() != EOF; ++nc)

;

printf("%.0f\n", nc); return(0);

}

Функция PRINTF использует спецификацию %F как для FLOAT, так и для DOUBLE;

спецификация %.0F подавляет печать несуществующей дробной части.

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

ратор FOR имел тело. Изолированная точка с запятой, соответствующая пустому операто-

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]