Керниган и Ритчи - Язык программирования Си (793773), страница 8
Текст из файла (страница 8)
Функция printf — лишь полезная функция стандартной библиотеки, котораяобычно доступна для Си-программ. Поведение функции printf, однако, оговорено стандартом ANSI, и еесвойства должны быть одинаковыми во всех Си-системах, удовлетворяющих требованиям стандарта.Желая сконцентрировать ваше внимание на самом Си, мы не будем много говорить о вводе-выводе до главы7.
В частности, мы отложим разговор о форматном вводе. Если вам потребуется ввести числа, советуемпрочитать в параграфе 7.4 то, что касается функции scanf. Эта функция отличается от printf лишь тем, чтоона вводит данные, а не выводит.Существуют еще две проблемы, связанные с программой преобразования температур. Одна из них (болеепростая) состоит в том, что выводимый результат выглядит несколько неряшливо, поскольку числа невыровнены по правой позиции колонок. Это легко исправить, добавив в каждый из спецификаторов формата%d указание о ширине поля; при этом программа будет печатать числа, прижимая их к правому краюуказанных полей.
Например, мы можем написатьprintf("%3d %6d\n", fahr, celsius);чтобы в каждой строке первое число печатать в поле из трех позиций, а второе — из шести. В результатебудет напечатано:0204060801002-17-64152637По-английски — truncation (усечение). — Примеч. корр.Вторая, более серьезная проблема связана с тем, что мы пользуемся целочисленной арифметикой и поэтомуне совсем точно вычисляем температуры по шкале Цельсия. Например, 0°F на самом деле (с точностью додесятой) равно -17.8 °С, а не -17. Чтобы получить более точные значения температур, нам надо пользоватьсяне целочисленной арифметикой, а арифметикой с плавающей точкой. Это потребует некоторых изменений впрограмме.#include <stdio.h>/* печать таблицы температур по Фаренгейту и Цельсию дляfahr = 0, 20 ...
300; вариант с плавающей точкой */main(){float fahr, celsius;int lower, upper; step;lower = 0; /* нижняя граница таблицы температур */upper = 300; /* верхняя граница */step = 20; /* шаг */fahr = lower;while (fahr <= upper) {celsius = (5.0/9.0) * (fahr-32.0);printf ("%3.0f %6.1f\n", fahr, celsius);fahr = fahr + step;}}Программа мало изменилась. Она отличается от предыдущей лишь тем, что fahr и celsius объявлены какfloat, а формула преобразования написана в более естественном виде. В предыдущем варианте нельзябыло писать 5/9, так как целочисленное деление в результате отбрасывания дробной части дало бы нуль.Десятичная точка в константе указывает на то, что последняя рассматривается как число с плавающей точкой,и 5.0/9.0, таким образом, есть частное от деления двух значений с плавающей точкой, которое непредполагает отбрасывания дробной части.
В том случае, когда арифметическая операция имеет целыеоперанды, она выполняется по правилам целочисленной арифметики. Если же один операнд с плавающейточкой, а другой — целый, то перед тем, как операция будет выполнена, последний будет преобразован вчисло с плавающей точкой. Если бы мы написали fahr-32, то 32 автоматически было бы преобразовано вчисло с плавающей точкой. Тем не менее при записи констант с плавающей точкой мы всегда используемдесятичную точку, причем даже в тех случаях, когда константы на самом деле имеют целые значения.
Этоделается для того, чтобы обратить внимание читающего программу на их природу.Более подробно правила, определяющие, в каких случаях целые переводятся в числа с плавающей точкой,рассматриваются в главе 2. А сейчас заметим, что присваиваниеfahr = lower;и проверкаwhile (fahr <= upper)работают естественным образом, т. е. перед выполнением операции значение int приводится к float.Спецификация %3.0f в printf определяет печать числа с плавающей точкой (в данном случае числа fahr)в поле шириной не более трех позиций без десятичной точки и дробной части. Спецификация %6.1fописывает печать другого числа (celsius) в поле из шести позиций с одной цифрой после десятичной точки.Напечатано будет следующее:02040-17.8-6.74.4Ширину и точность можно не задавать: %6f означает, что число будет занимать не более шести позиций;%.2f — число имеет две цифры после десятичной точки, но ширина не ограничена; %f просто указывает напечать числа с плавающей точкой.%d— печать десятичного целого.%6d— печать десятичного целого в поле из шести позиций.%f— печать числа с плавающей точкой.%6f— печать числа с плавающей точкой в поле из шести позиций.%.2f— печать числа с плавающей точкой с двумя цифрами после десятичной точки.%6.2f— печать числа с плавающей точкой и двумя цифрами после десятичной точки в поле из шестипозиций.Кроме того, printf допускает следующие спецификаторы: %o для восьмеричного числа; %х дляшестнадцатеричного числа; %с для символа; %s для строки символов и %% для самого %.Упражнение 1.3.
Усовершенствуйте программу преобразования температур таким образом, чтобы надтаблицей она печатала заголовок.Упражнение 1.4. Напишите программу, которая будет печатать таблицу соответствия температур по Цельсиютемпературам по Фаренгейту.1.3. Инструкция forСуществует много разных способов для написания одной и той же программы. Видоизменим нашу программупреобразования температур:#include <stdio.h>/* печать таблицы температур по Фаренгейту и Цельсию */main(){int fahr;for (fahr = 0; fahr <= 300; fahr = fahr + 20)printf ("%3d %6.1f\n", fahr, (5.0/9.0)*(fahr-32));}Эта программа печатает тот же результат, но выглядит она, несомненно, по-другому.
Главное отличиезаключается в отсутствии большинства переменных. Осталась только переменная fahr , которую мыобъявили как int. Нижняя и верхняя границы и шаг присутствуют в виде констант в инструкции for — новойдля нас конструкции, а выражение, вычисляющее температуру по Цельсию, теперь задано третьимаргументом функции printf, а не в отдельной инструкции присваивания.Последнее изменение является примером применения общего правила: в любом контексте, где возможноиспользовать значение переменной какого-то типа, можно использовать более сложное выражение того жетипа.
Так, на месте третьего аргумента функции printf согласно спецификатору %6.1f должно бытьзначение с плавающей точкой, следовательно, здесь может быть любое выражение этого типа.Инструкция for описывает цикл, который является обобщением цикла while. Если вы сравните его с ранеенаписанным while, то вам станет ясно, как он работает.
Внутри скобок имеются три выражения,разделяемые точкой с запятой. Первое выражение — инициализацияfahr = 0выполняется один раз перед тем, как войти в цикл. Второе — проверка условия продолжения циклаfahr <= 300Условие вычисляется, и если оно истинно, выполняется тело цикла (в нашем случае это одно обращение кprintf). Затем осуществляется приращение шага:fahr = fahr + 20и условие вычисляется снова.
Цикл заканчивается, когда условие становится ложным. Как и в случае с while,тело for-цикла может состоять из одной инструкции или из нескольких, заключенных в фигурные скобки. Наместе этих трех выражений (инициализации, условия и приращения шага) могут стоять произвольныевыражения.Выбор между while и fог определяется соображениями ясности программы.
Цикл for более удобен в техслучаях, когда инициализация и приращение шага логически связаны друг с другом общей переменной ивыражаются единичными инструкциями, поскольку названный цикл компактнее цикла while, а егоуправляющие части сосредоточены в одном месте.Упражнение 1.5. Измените программу преобразования температур так, чтобы она печатала таблицу вобратном порядке, т. е. от 300 до 0.1.4.
Именованные константыПрежде чем мы закончим рассмотрение программы преобразования температур, выскажем еще односоображение. Очень плохо, когда по программе рассеяны "загадочные числа", такие как 300, 20. Тот, ктобудет читать программу, не найдет в них и намека на то, что они собой представляют. Кроме того, их труднозаменить на другие каким-то систематическим способом. Одна из возможностей справиться с такимичислами — дать им осмысленные имена.
Строка #define определяет символьное имя, или именованнуюконстанту, для заданной строки символов:#define имя подставляемый-текстС этого момента при любом появлении имени (если только оно встречается не в тексте, заключенном вкавычки, и не является частью определения другого имени) оно будет заменяться на соответствующий емуподставляемый-текст. Имя имеет тот же вид, что и переменная: последовательность букв и цифр,начинающаяся с буквы. Подставляемый-текст может быть любой последовательностью символов, средикоторых могут встречаться не только цифры.#include <stdio.h>#define LOWER 0 /* нижняя граница таблицы */#define UPPER 300 /* верхняя граница */#define STEP 20 /* размер шага *//* печать таблицы температур по Фаренгейту и Цельсию */main (){int fahr;for (fahr = LOWER; fahr <= UPPER; fahr = fahr + STEP)printf ("%3d %6.1f\n", fahr, (5.0/9.0)*(fahr-32));}Величины LOWER, UPPER и STEP — именованные константы, а не переменные, поэтому для них нетобъявлений.
По общепринятому соглашению имена именованных констант набираются заглавными буквами,чтобы они отличались от обычных переменных, набираемых строчными. Заметим, что в конце #defineстроки точка с запятой не ставится.1.5. Ввод-вывод символовТеперь мы намерены рассмотреть семейство программ по обработке текстов. Вы обнаружите, что многиесуществующие программы являются просто расширенными версиями обсуждаемых здесь прототипов.Стандартная библиотека поддерживает очень простую мрдель ввода-вывода. Текстовый ввод-вывод внезависимости от того, откуда он исходит или куда направляется, имеет дело с потоком символов.
Текстовыйпоток — это последовательность символов, разбитая на строки, каждая из которых содержит нуль или болеесимволов и завершается символом новой строки. Обязанность следить за тем, чтобы любой поток вводавывода отвечал этой модели, возложена на библиотеку: программист, пользуясь библиотекой, не должензаботиться о том, в каком виде строки представляются вне программы.Стандартная библиотека включает несколько функций для чтения и записи одного символа.
Простейшие изних — getchar и putchar. За одно обращение к getchar считывается следующий символ ввода изтекстового потока, и этот символ выдается в качестве результата. Так, после выполненияс = getchar()переменная c содержит очередной символ ввода. Обычно символы поступают с клавиатуры. Ввод из файловрассматривается в главе 7.Обращение к putchar приводит к печати одного символа.
Так,putchar (c)напечатает содержимое целой переменной c в виде символа (обычно на экране). Вызовы putchar иprintf могут произвольным образом перемежаться. Вывод будет формироваться в том же порядке, что иобращения к этим функциям.1.5.1. Копирование файлаПри наличии функций getchar и putchar, ничего больше не зная о вводе-выводе, можно написатьудивительно много полезных программ. Простейший пример — это программа, копирующая по одномусимволу с входного потока в выходной поток:чтение символаwhile (символ не является признаком конца файла)вывод только что прочитанного символачтение символаОформляя ее в виде программы на Си, получим#include <stdio.h>/* копирование ввода на вывод; 1-я версия */main(){int с;с = getchar();while (с != EOF) {putchar (c);с = getchar();}}Оператор отношения != означает "не равно".Каждый символ, вводимый с клавиатуры или появляющийся на экране, как и любой другой символ внутримашины, кодируется комбинацией битов.
Тип char специально предназначен для хранения символьныхданных, однако для этого также годится и любой целый тип. Мы пользуемся типом int и делаем это поодной важной причине, которая требует разъяснений.Существует проблема: как отличить конец ввода от обычных читаемых данных. Решение заключается в том,чтобы функция getchar по исчерпании входного потока выдавала в качестве результата такое значение,которое нельзя было бы спутать ни с одним реальным символом. Это значение есть EOF (аббревиатура от endof file — конец файла). Мы должны объявить переменную с такого типа, чтобы его "хватило" дляпредставления всех возможных результатов, выдаваемых функцией getchar. Нам не подходит тип char,так как c должна быть достаточно "емкой", чтобы помимо любого значения типа char быть в состояниихранить и EOF.














