Керниган и Ритчи - Язык программирования Си (793773), страница 37
Текст из файла (страница 37)
Здесь мы покажем наиболее типичные случаиприменения этой функции; полное ее описание дано в приложении В.Функция printf преобразует, форматирует и печатает свои аргументы в стандартном выводе подуправлением формата. Возвращает она количество напечатанных символов.Форматная строка содержит два вида объектов: обычные символы, которые впрямую копируются в выходнойпоток, и спецификации преобразования, каждая из которых вызывает преобразование и печать очередногоаргумента printf.
Любая спецификация преобразования начинается знаком % и заканчивается символомспецификатором. Между % и символом-спецификатором могут быть расположены (в указанном нижепорядке) следующие элементы:Знак минус, предписывающий выравнивать преобразованный аргумент по левому краю поля.Число, специфицирующее минимальную ширину поля. Преобразованный аргумент будет заниматьполе по крайней мере указанной ширины. При необходимости лишние позиции слева (или справа прилевостороннем расположении) будут заполнены пробелами.Точка, отделяющая ширину поля от величины, устанавливающей точность.Число (точность), специфицирующее максимальное количество печатаемых символов в строке, иликоличество цифр после десятичной точки для чисел с плавающей запятой, или минимальноеколичество цифр для целого.Буква h, если печатаемое целое должно рассматриваться как short, или l (латинская буква ell), еслицелое должно рассматриваться как long.Символы-спецификаторы перечислены в таблице 7.1.
Если за % не помещен символ-спецификатор, поведениефункции printf будет не определено.Ширину и точность можно специфицировать с помощью *; значение ширины (или точности) в этом случаеберется из следующего аргумента (который должен быть типа int). Например, чтобы напечатать не болееmax символов из строки s, годится следующая запись:printf("%.*s", max, s);Большая часть форматных преобразований была продемонстрирована в предыдущих главах.
Исключениесоставляет задание точности для строк. Далее приводится перечень спецификаций и показывается их влияниена печать строки "hello, world", состоящей из 12 символов. Поле специально обрамлено двоеточиями, чтобыбыла видна его протяженность.:%s::%10s::%.10s::%-10s::%.15s::%-15s::%15.10s::hello, world::hello, world::hello, wor::hello, world::hello, world::hello, world::hello, wor::%-15.10s::hello, wor:Таблица 7.1. Основные преобразования printfСимволТип аргумента; вид печатиd, iint; десятичное целоеоint; беззнаковое восьмеричное (octal) целое (без нуля слева)х, Xunsigned int; беззнаковое шестнадцатеричное целое (без 0х или 0Х слева), для 10...15используются abcdef или ABCDEFuint;беззнаковое десятичное целоеcint; одиночный символschar *; печатает символы, расположенные до знака \0, или в количестве, заданномточностьюfdouble; [-+ m.dddddd, где количество цифр d задается точностью (по умолчанию равно 6)e, Edouble; [-] m.dddddde+хх или [-] m.ddddddE±xx, где количество цифр d задается точностью(по умолчанию равно 6)g, Gdouble; использует %е или %Е, если порядок меньше, чем -4, или больше или равенточности; в противном случае использует %f.
Завершающие нули и завершающаядесятичная точка не печатаютсяpvoid *; указатель (представление зависит от реализации)%Аргумент не преобразуется; печатается знак %Предостережение: функция printf использует свой первый аргумент, чтобы определить, сколько ещеожидается аргументов и какого они будут типа.
Вы не получите правильного результата, если аргументовбудет не хватать или они будут принадлежать не тому типу. Вы должны также понимать разницу в следующихдвух обращениях:printf(s); /* НЕВЕРНО, если в s есть %, */printf("%s", s); /* ВЕРНО всегда */Функция sprintf выполняет те же преобразования, что и printf, но вывод запоминает в строкеint sprintf(char *string, char *format, arg1, arg2, ...)Эта функция форматирует arg1, arg2 и т.д. в соответствии с информацией, заданной аргументом format, какмы описывали ранее, но результат помещает не в стандартный вывод, а в string.
Заметим, что строкаstring должна быть достаточно большой, чтобы в ней поместился результат.Упражнение 7.2. Напишите программу, которая будет печатать разумным способом любой ввод. Какминимум она должна уметь печатать неграфические символы в восьмеричном или шестнадцатеричном виде(в форме, принятой на вашей машине), обрывая длинные текстовые строки.7.3. Списки аргументов переменной длиныЭтот параграф содержит реализацию минимальной версии printf. Приводится она для того, чтобыпоказать, как надо писать функции со списками аргументов переменной длины, причем такие, которые былибы переносимы. Поскольку нас главным образом интересует обработка аргументов, функцию minprintfнапишем таким образом, что она в основном будет работать с задающей формат строкой и аргументами; чтоже касается форматных преобразований, то они будут осуществляться с помощью стандартного printf.Объявление стандартной функции printf выглядит так:int printf(char *fmt, ...)Многоточие в объявлении означает, что число и типы аргументов могут изменяться.
Знак многоточие можетстоять только в конце списка аргументов. Наша функция minprintf объявляется какvoid minprintf(char *fmt, ...)поскольку она не будет выдавать число символов, как это делает printf.Вся сложность в том, каким образом minprintf будет продвигаться вдоль списка аргументов, — ведь уэтого списка нет даже имени. Стандартный заголовочный файл <stdarg.h> содержит набормакроопределений, которые устанавливают, как шагать по списку аргументов. Наполнение этогозаголовочного файла может изменяться от машины к машине, но представленный им интерфейс вездеодинаков.Тип va_list служит для описания переменной, которая будет по очереди указывать на каждый изаргументов; в minprintf эта переменная имеет имя ap (от "argument pointer" — указатель на аргумент).Макрос va_start инициализирует переменную ap, чтобы она указывала на первый безымянный аргумент.К va_start нужно обратиться до первого использования ap.
Среди аргументов по крайней мере одиндолжен быть именованным; от последнего именованного аргумента этот макрос "отталкивается" приначальной установке.Макрос va_arg на каждом своем вызове выдает очередной аргумент, а ap передвигает на следующий; поимени типа он определяет тип возвращаемого значения и размер шага для выхода на следующий аргумент.Наконец, макрос va_end делает очистку всего, что необходимо. К va_end следует обратиться перед самымвыходом из функции.Перечисленные средства образуют основу нашей упрощенной версии printf.#include <stdarg.h>/* minprintf: минимальный printf с переменным числом аргумент */void minprintf(char *fmt, ...){va_list ар; /* указывает на очередной безымянный аргумент */char *p, *sval;int ival;double dval;va_start(ap, fmt); /* устанавливает ар на 1-й безымянный аргумент */for (p = fmt; *р; р++) {if (*p != '%') {putchar(*p);continue;}switch (*++р) {case 'd':ival = va_arg(ap, int);printf ("%d", ival);break;case 'f':dval = va_arg(ap, double);printf("%f", dval);break;case 's':for (sval = va_arg(ap, char *); *sval; sval++)putchar(*sval);break;default:putchar(*p);break;}}va_end(ap); /* очистка, когда все сделано */}Упражнение 7.3.
Дополните minprintf другими возможностями printf.7.4. Форматный ввод (scanf)Функция scanf, обеспечивающая ввод, является аналогом printf; она выполняет многие изупоминавшихся преобразований, но в противоположном направлении. Ее объявление имеет следующий вид:int scanf(char *format, ...)Функция scanf читает символы из стандартного входного потока, интерпретирует их согласноспецификациям строки format и рассылает результаты в свои остальные аргументы. Аргумент-формат мыопишем позже; другие аргументы, каждый из которых должен быть указателем, определяют, где будутзапоминаться должным образом преобразованные данные. Как и для printf, в этом параграфе даетсясводка наиболее полезных, но отнюдь не вдех возможностей данной функции.Функция scanf прекращает работу, когда оказывается, что исчерпался формат или вводимая величина несоответствует управляющей спецификации.
В качестве результата scanf возвращает количество успешновведенных элементов данных. По исчерпании файла она выдает EOF. Существенно то, что значение EOF неравно нулю, поскольку нуль scanf выдает, когда вводимый символ не соответствует первой спецификацииформатной строки. Каждое очередное обращение к scanf продолжает ввод с символа, следующего сразу запоследним обработанным.Существует также функция sscanf, которая читает из строки (а не из стандартного ввода).int sscanf(char *string, char *format, arg1, arg2, ...)Функция sscanf просматривает строку string согласно формату format и рассылает полученныезначения в arg1, arg2 и т. д. Последние должны быть указателями.Формат обычно содержит спецификации, которые используются для управления преобразованиями ввода.
Внего могут входить следующие элементы:Пробелы или табуляции, которые игнорируются.Обычные символы (исключая %), которые, как ожидается, совпадут с очередными символами,отличными от символов-разделителей входного потока.Спецификации преобразования, каждая из которых начинается со знака % и завершается символомспецификатором типа преобразования. В промежутке между этими двумя символами в любойспецификации могут располагаться, причем в том порядке, как они здесь указаны: знак * (признакподавления присваивания); число, определяющее ширину поля; буква h, l или L, указывающая наразмер получаемого значения; и символ преобразования (о, d, x).Спецификация преобразования управляет преобразованием следующего вводимого поля.
Обычно результатпомещается в переменную, на которую указывает соответствующий аргумент. Однако если в спецификациипреобразования присутствует *, то поле ввода пропускается и никакое присваивание не выполняется. Полеввода определяется как строка без символов-разделителей; оно простирается до следующего символаразделителя или же ограничено шириной поля, если она задана. Поскольку символ новой строки относится ксимволам-разделителям, то scanf при чтении будет переходить с одной строки на другую.














