| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357 |
- \chapter{Ввод-вывод}
- \label{chapt:input-output}
- \index{ввод-вывод}%
- Возможности ввода-вывода не являются частью самого языка Си, поэтому мы подробно
- и не рассматривали их до сих пор. Между тем реальные программы взаимодействуют
- со своим окружением гораздо более сложным способом, чем те, которые были
- затронуты ранее. В этой главе мы опишем стандартную библиотеку, набор функций,
- обеспечивающих ввод-вывод, работу со стрингами, управление памятью, стандартные
- математические функции и разного рода сервисные Си-программы. Но особое внимание
- уделим вводу-выводу.
- \index{переносимость}%
- Библиотечные функции ввода-вывода точно определяются стандартом ANSI, так что
- они совместимы на любых установках, где поддерживается Си. Программы, которые в
- своём взаимодействии с системным окружением не выходят за рамки возможностей
- стандартной библиотеки, можно без изменений переносить с одной машины на другую.
- Свойства библиотечных функций специфицированы в более чем дюжине головных
- файлов;
- \index{файл!головной!<stdio.h>@\texttt{<stdio.h>}}%
- \index{stdio.h@\texttt{<stdio.h>}}%
- вам уже встречались некоторые из них, в том числе \verb|<stdio.h>|,
- \verb|<string.h>| и \verb|<ctype.h>|. Мы не рассматриваем здесь всю библиотеку,
- так как нас больше интересует написание Си-программ, чем использование
- библиотечных функций. Стандартная библиотека подробно описана в
- приложении~\ref{apx:stdlib}.
- \section{Стандартный ввод-вывод}
- \label{chapt:input-output:sec:standard_input-output}
- Как уже говорилось в гл.~\ref{chapt:tutorial_intro}, библиотечные функции
- реализуют простую модель текстового ввода-вывода.
- \index{поток!текстовый}%
- \index{текстовый поток}%
- Текстовый поток состоит из последовательности строк; каждая строка заканчивается
- литерой новая-строка. Если система в чём-то не следует принятой модели,
- библиотека сделает так, чтобы казалось, что эта модель удовлетворяется
- полностью. Например, пара литер -- возврат-каретки и перевод-строки -- при вводе
- могла бы быть преобразована в одну литеру новая-строка, а при выводе выполнялось
- бы обратное преобразование.
- Простейший механизм ввода -- это
- \index{ввод-вывод!литер}%
- чтение одной литеры из
- \index{ввод!с клавиатуры}%
- \index{стандартный!ввод}%
- \emph{стандартного ввода} (обычно с клавиатуры) функцией
- \index{библиотечная функция!getchar@\texttt{getchar}}%
- \verb|getchar|:
- \begin{ShortCodePar}
- int getchar(void)
- \end{ShortCodePar}
- \noindent В качестве результата каждого своего вызова функция \verb|getchar|
- возвращает следующую литеру ввода или, если обнаружен конец файла, \verb|EOF|.
- \index{файл!головной!<stdio.h>@\texttt{<stdio.h>}}%
- \index{EOF@\texttt{EOF}}%
- \index{stdio.h@\texttt{<stdio.h>}}%
- Именованная константа \verb|EOF| (аббревиатура от end of file -- конец файла)
- определена в \verb|<stdio.h>|. Обычно значение \verb|EOF| равно $-1$, но, чтобы
- не зависеть от конкретного значения этой константы, ссылаться на неё следует по
- имени (\verb|EOF|).
- \index{ввод-вывод!перенаправление}%
- Во многих системах клавиатуру можно заменить файлом, перенаправив ввод при
- помощи значка \verb|<|. Так, если программа \verb|prog| использует
- \verb|getchar|, то командная строка
- \begin{ShortCodePar}
- prog <infile
- \end{ShortCodePar}
- \noindent предпишет программе \verb|prog| читать литеры из \verb|infile|, а не с
- клавиатуры. Переключение ввода делается так, что сама программа \verb|prog| не
- замечает подмены; в частности, стринг \verb|"<infile"| не будет включён в
- аргументы командной строки \verb|argv|.
- \index{трубопроводный механизм}%
- Переключение ввода будет также незаметным, если ввод исходит от другой программы
- и передаётся через <<трубопроводный>> механизм. В некоторых системах командная
- строка
- \begin{ShortCodePar}
- otherprog | prog
- \end{ShortCodePar}
- \noindent приведёт к тому, что две программы, \verb|otherprog| и \verb|prog|,
- соединятся напрямую, т.е. стандартный вывод \verb|otherprog| станет стандартным
- вводом для \verb|prog|.
- Функция
- \begin{ShortCodePar}
- int putchar(int)
- \end{ShortCodePar}
- \noindent используется для вывода: \verb|putchar(c)| отправляет литеру \verb|c|
- \index{стандартный!вывод}%
- в \emph{стандартный вывод}, под которым по умолчанию подразумевается
- \index{вывод!на экран}%
- экран. Функция \verb|putchar| в качестве результата возвращает посланную литеру
- или, в случае ошибки, \verb|EOF|. То же и в отношении вывода:
- \index{вывод!перенаправление}%
- при помощи записи вида \verb|>|\textit{\Verb|имя-файла|} вывод можно
- перенаправить в файл. Например, если \verb|prog| использует для вывода функцию
- \verb|putchar|, то
- \begin{ShortCodePar}
- prog >outfile
- \end{ShortCodePar}
- \noindent будет направлять стандартный вывод не на экран, а в \verb|outfile|. А
- командная строка
- \begin{ShortCodePar}
- prog | anotherprog
- \end{ShortCodePar}
- \noindent соединит стандартный вывод программы \verb|prog| со стандартным вводом
- программы \verb|anotherprog|.
- Вывод, осуществляемый функцией \verb|printf|, также отправляется в стандартный
- выходной поток. Вызовы \verb|putchar| и \verb|printf| могут как угодно
- чередоваться, при этом вывод будет формироваться в той последовательности, в
- которой происходили вызовы этих функций.
- \index{файл!головной!<stdio.h>@\texttt{<stdio.h>}}%
- \index{include@\texttt{{\#}include}}%
- \index{stdio.h@\texttt{<stdio.h>}}%
- Любой исходный Си-файл, использующий хотя бы одну функцию библиотеки
- ввода-вывода, должен содержать в себе строку
- \begin{ShortCodePar}
- #include <stdio.h>
- \end{ShortCodePar}
- \noindent причём она должна быть расположена до первого обращения к
- вводу-выводу. Если имя головного файла заключено в угловые скобки \verb|<| и
- \verb|>|, это значит, что поиск головного файла ведётся в стандартном месте
- (например, в системе UNIX это обычно директория \verb|/usr/include|).
- Многие программы читают только из одного входного потока и пишут только в один
- выходной поток. Для организации ввода-вывода таким программам вполне хватит
- функций \verb|getchar|, \verb|putchar| и \verb|printf|, а для начального
- обучения ознакомления с этими функциями уж точно достаточно. В частности,
- перечисленных функций достаточно, когда требуется вывод одной программы
- соединить с вводом следующей.
- \index{программа!перевода в нижний регистр}%
- В качестве примера рассмотрим программу \verb|lower|, переводящую свой ввод на
- нижний регистр:
- \begin{LongCodePar}
- #include <stdio.h>
- #include <ctype.h>
- main() /* lower: переводит ввод на нижний регистр */
- {
- int c;
- while ((c = getchar()) != EOF)
- putchar(tolower(c));
- return 0;
- }
- \end{LongCodePar}
- Функция \verb|tolower| определена в \verb|<ctype.h>|. Она переводит буквы
- верхнего регистра в буквы нижнего регистра, а остальные литеры возвращает без
- изменений. Как мы уже упоминали, <<функции>> типа \verb|getchar| и
- \verb|putchar| из библиотеки \verb|<stdio.h>| и функция \verb|tolower| из
- библиотеки \verb|<ctype.h>| часто реализуют в виде макросов, чтобы исключить
- накладные расходы от вызова функции на каждую отдельную литеру. В
- разд.~\ref{sec:fopen_and_getc} мы покажем, как это делается. Независимо от того,
- как на той или иной машине реализованы функции библиотеки \verb|<ctype.h>|‚
- использующие их программы могут ничего не знать о кодировке литер.
- \paragraph{Упражнение 7.1.} Напишите программу, осуществляющую перевод ввода с
- верхнего регистра на нижний или с нижнего на верхний в зависимости от имени, по
- которому она вызывается и текст которого находится в \verb|argv[0]|.
- %
- % исправлена опечатка
- % в оригинале ``arg[0]''
- %
- \section{Форматный вывод (\texorpdfstring{\protect\Verb|printf|}{printf})}
- Функция
- \index{библиотечная функция!printf@\texttt{printf}}%
- \verb|printf| переводит внутренние значения в текст.
- \begin{ShortCodeParWithCC}{\\\{\}}
- int printf(char *format, \textit{arg}\textsubscript{1},\
- \textit{arg}\textsubscript{2}, ...)
- \end{ShortCodeParWithCC}
- В предыдущих главах мы использовали \verb|printf| неформально. Здесь мы покажем
- наиболее типичные случаи применения этой функции; полное её описание дано в
- приложении~\ref{apx:stdlib}.
- Функция \verb|printf| преобразует, форматирует и печатает свои аргументы в
- стандартном выводе под управлением формата. Возвращает она количество
- напечатанных литер.
- Форматный стринг содержит два вида объектов: обычные литеры, которые впрямую
- копируются в выходной поток, и спецификации преобразования, каждая из которых
- вызывает преобразование и печать очередного аргумента \verb|printf|. Любая
- спецификация преобразования начинается знаком \verb|%| и заканчивается
- литерой-спецификатором. Между \verb|%| и литерой-спецификатором могут быть
- расположены (в указанном ниже порядке) следующие элементы:
- \begin{itemize}
- \item Знак минус, предписывающий <<прижать>> преобразованный аргумент к левому
- краю поля.
- \item Число, специфицирующее минимальную ширину поля. Преобразованный аргумент
- будет занимать поле по крайней мере указанной ширины. При необходимости лишние
- позиции слева (или справа при левостороннем расположении) будут заполнены
- пробелами.
- \item Точка, отделяющая ширину поля от величины, устанавливающей точность.
- \item Число (точность), специфицирующее максимальное количество печатаемых
- литер стринга, или количество цифр после десятичной точки -- для плавающего
- значения, или минимальное количество цифр -- для целого.
- \item Буква \verb|h|, если печатаемое целое должно рассматриваться как
- \verb|short|, или \verb|l| (латинская буква эль), если целое должно
- рассматриваться как \verb|long|.
- \end{itemize}
- \FloatBarrier
- \begin{table}[t]
- \captionsetup{name=ТАБЛИЦА,labelsep=period,justification=centering}
- \renewcommand\thetable{7.1}
- \caption{ОСНОВНЫЕ ПРЕОБРАЗОВАНИЯ PRINTF}
- \label{tab:printf_chapt7}
- \index{таблица!преобразований в \texttt{printf}}%
- \begin{tabular*}{\linewidth}
- {p{0.1118012\linewidth-4\tabcolsep}|p{0.8881988\linewidth-4\tabcolsep}}
- \hline\hline
- \multicolumn{1}{c|}{\rule{0pt}{3ex}ЛИТЕРА\rule[-1.5ex]{0pt}{0pt}}&
- \multicolumn{1}{|c}{ТИП АРГУМЕНТА; ВИД ПЕЧАТИ} \\
- \hline
- \rule{0pt}{3ex}\verb| d, i| &\verb|int|; десятичное целое. \\
- \verb| o| &\verb|int|; беззнаковое восьмеричное (octal) целое (без ведущего
- нуля). \\
- \verb| x, X| &\verb|int|; беззнаковое шестнадцатеричное целое (без ведущих
- \verb|0x| и \verb|0X|), для $10\ldots15$ используются \verb|abcdef| или
- \verb|ABCDEF|. \\
- \verb| u| &\verb|int|; беззнаковое десятичное целое. \\
- \verb| c| &\verb|int|; одиночная литера. \\
- \verb| s| &\verb|char *|; печатает литеры, расположенные до знака \verb|\0|,
- или в количестве, заданном точностью. \\
- \verb| f| &\verb|double|;
- \verb|[-]|\textit{\Verb|m|}\verb|.|\textit{\Verb|dddddd|}, где количество цифр
- \textit{d} задаётся точностью (по умолчанию равно 6). \\
- \verb| e, E| &\verb|double|;
- \verb|[-]|\textit{\Verb|m|}\verb|.|\textit{\Verb|dddddd|}\texttt{e\textpm}\textit{\Verb|xx|}
- или
- \verb|[-]|\textit{\Verb|m|}\verb|.|\textit{\Verb|dddddd|}\texttt{E\textpm}\textit{\Verb|xx|}, где количество цифр
- \textit{d} задаётся точностью (по умолчанию равно 6). \\
- \verb| g, G| &\verb|double|; использует \verb|%e| или \verb|%E|, если экспонента
- меньше, чем $-4$‚ или больше или равна точности; в противном случае использует
- \verb|%f|. <<Хвостовые>> нули и <<хвостовая>> десятичная точка не печатаются. \\
- \verb| p| &\verb|void *|; указатель (представление зависит от реализации). \\
- \rule[-1.5ex]{0pt}{0pt}\verb| %| &аргумент не преобразуется; печатается знак
- \verb|%|. \\
- \hline
- \end{tabular*}
- \end{table}
- %
- % исправлена орфографическая ошибка
- % в оригинале было ``шестнадцатиричное''
- %
- \noindent Литеры-спецификаторы перечислены в табл.~\ref{tab:printf_chapt7}. Если
- за \verb|%| не помещена литера-спецификатор, поведение функции \verb|printf|
- будет не определено.
- Ширину и точность можно специфицировать при помощи \verb|*|; значение ширины
- (или точности) в этом случае берётся из следующего аргумента (который должен
- быть типа \verb|int|). Например, чтобы напечатать не более \verb|max| литер из
- стринга \verb|s|, годится следующая запись:
- \begin{ShortCodePar}
- printf("%.*s", max, s);
- \end{ShortCodePar}
- Б\'{o}льшая часть форматных преобразований была продемонстрирована в предыдущих
- главах. Исключение составляет задание точности для стрингов. Далее приводится
- перечень спецификаций и показывается их влияние на печать стринга
- \verb|"hello, world"|, состоящего из 12 литер. Поле специально обрамлено
- двоеточиями, чтобы была видна его протяжённость.
- \begin{LongCodePar}
- :%s: :hello, world:
- :%10s: :hello, world:
- :%.10s: :hello, wor:
- :%-10s: :hello, world:
- :%.15s: :hello, world:
- :%-15s: :hello, world :
- :%15.10s: : hello, wor:
- :%-15.10s: :hello, wor :
- \end{LongCodePar}
- \noindent Предостережение: функция \verb|printf| использует свой первый
- аргумент, чтобы определить, сколько ещё ожидается аргументов и какого они будут
- типа. Вы не получите правильного результата, если аргументов будет не хватать
- или они будут принадлежать не тому типу. Вы должны также понимать разницу в
- следующих двух обращениях:
- \begin{ShortCodePar}
- printf(s); /* НЕВЕРНО, если в s есть % */
- printf("%s", s); /* ВЕРНО всегда */
- \end{ShortCodePar}
- Функция
- \index{библиотечная функция!sprintf@\texttt{sprintf}}%
- \verb|sprintf| выполняет те же преобразования, что и \verb|printf|, но вывод
- запоминает в стринге
- \begin{ShortCodeParWithCC}{\\\{\}}
- int sprintf(char *string, char *format, \textit{arg}\textsubscript{1},\
- \textit{arg}\textsubscript{2}, ...)
- \end{ShortCodeParWithCC}
- \noindent Эта функция форматирует \textit{arg}$_{1}$, \textit{arg}$_{2}$ и т.д.
- в соответствии с информацией, заданной аргументом \verb|format| так, как мы
- ранее описывали, но результат помещает не в стандартный вывод, а в
- \verb|string|. Заметим, что \verb|string| должен быть достаточно большим, чтобы
- в нём поместился результат.
- %
- % нельзя не отметить, что в предшествующем абзаце существует некоторая
- % неопределённость относительно того, что имеется в виду под string
- %
- \paragraph{Упражнение 7.2.} Напишите программу, которая будет печатать разумным
- способом любой ввод. Как минимум она должна уметь печатать неграфические литеры
- в восьмеричном или шестнадцатеричном виде (в форме, принятой на вашей машине),
- обрывая длинные текстовые строки.
- %
- % исправлена орфографическая ошибка
- % в оригинале было ``шестнадцатиричном''
- \section{Списки аргументов переменной длины}
- \index{список!аргументов переменной длины}%
- Этот раздел содержит реализацию минимальной версии \verb|printf|. Приводится она
- для того, чтобы показать‚ как надо писать функции со списками аргументов
- переменной длины, причём такие, которые были бы переносимы. Поскольку нас
- главным образом интересует обработка аргументов, функцию \verb|minprintf|
- напишем таким образом, что она в основном будет работать со стрингом, задающим
- формат, и аргументами; что же касается форматных преобразований, то они будут
- осуществляться при помощи стандартного \verb|printf|.
- Декларация стандартной функции \verb|printf| выглядит так:
- \begin{ShortCodePar}
- int printf(char *fmt, ...)
- \end{ShortCodePar}
- \noindent Многоточие в декларации означает, что число и типы аргументов могут
- изменяться. Знак многоточие может стоять только в конце списка аргументов. Наша
- функция \verb|minprintf| декларируется как
- \begin{ShortCodePar}
- void minprintf(char *fmt, ...)
- \end{ShortCodePar}
- \noindent поскольку она не будет выдавать число литер, как это делает
- \verb|printf|.
- Вся сложность в том, каким образом \verb|minprintf| будет продвигаться вдоль
- списка аргументов, -- ведь у этого списка нет даже имени.
- \index{файл!головной!<stdarg.h>@\texttt{<stdarg.h>}}%
- Стандартный головной файл \verb|<stdarg.h>| содержит набор макроопределений,
- которые определяют, как шагать по списку аргументов. Наполнение этого головного
- файла может изменяться от машины к машине, но представленный им интерфейс везде
- одинаков.
- \index{va{\_}list, va{\_}start, va{\_}arg, va{\_}end@\texttt{va{\_}list}, \texttt{va{\_}start}, \texttt{va{\_}arg}, \texttt{va{\_}end}}%
- Тип \verb|va_list| служит для описания переменной, которая будет по очереди
- ссылаться на каждый из аргументов; в \verb|minprintf| эта переменная имеет имя
- \verb|ap| (от <<argument pointer>> -- указатель на аргумент). Макрос
- \verb|va_start| инициализирует переменную \verb|ap|, чтобы она указывала на
- первый безымянный аргумент. К \verb|va_start| нужно обратиться до первого
- использования \verb|ap|. Среди аргументов по крайней мере один должен быть
- именованным; от последнего именованного аргумента этот макрос <<отталкивается>>
- при начальной установке.
- Макрос \verb|va_arg| на каждом своём вызове выдаёт очередной аргумент, а
- \verb|ap| передвигает на следующий; по имени типа он определяет тип
- возвращаемого значения и размер шага для выхода на следующий аргумент. Наконец,
- макрос \verb|va_end| делает очистку всего, что необходимо. К \verb|va_end|
- следует обратиться перед самым выходом из функции.
- Перечисленные средства образуют основу нашей упрощённой версии \verb|printf|.
- \begin{LongCodePar}
- #include <stdarg.h>
- /* minprintf: минимальный printf с переменным числом арг. */
- void minprintf(char *fmt, ...)
- {
- va_list ap; /* указывает на очередн. безымянный арг. */
- char *p, *sval;
- int ival;
- double dval;
- va_start(ap,fmt); /* ap указ-ет на 1-й безымянный арг. */
- for (p = fmt; *p; p++) {
- if (*p != '%') {
- putchar(*p);
- continue;
- }
- switch (*++p) {
- 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); /* очистка, когда всё сделано */
- }
- \end{LongCodePar}
- %
- % для целей выравнивания в предыдущем блоке кода
- % строка "va_start(ap, fmt);"
- % изменена на "va_start(ap,fmt);"
- % , что не повлияло на смысл кода
- %
- \paragraph{Упражнение 7.3.} Дополните \verb|minprintf| другими возможностями
- \verb|printf|.
- \section{Форматный ввод (\texorpdfstring{\protect\Verb|scanf|}{scanf})}
- \label{sec:formatted-input}
- Функция
- \index{библиотечная функция!scanf@\texttt{scanf}}%
- \verb|scanf|, обеспечивающая ввод, является аналогом \verb|printf|; она
- выполняет многие из упоминавшихся преобразований, но в противоположном
- направлении. Её декларация имеет следующий вид:
- \begin{ShortCodePar}
- int scanf(char *format, ...)
- \end{ShortCodePar}
- \FloatBarrier
- \begin{table}[t]
- \captionsetup{name=ТАБЛИЦА,labelsep=period,justification=centering}
- \renewcommand\thetable{7.2}
- \caption{ОСНОВНЫЕ ПРЕОБРАЗОВАНИЯ SCANF}
- \label{tab:scanf_chapt7}
- \index{таблица!преобразований в \texttt{scanf}}%
- \begin{tabular*}{\linewidth}
- {p{0.1118012\linewidth-4\tabcolsep}|p{0.8881988\linewidth-4\tabcolsep}}
- \hline\hline
- \multicolumn{1}{c|}{\rule{0pt}{3ex}ЛИТЕРА\rule[-1.5ex]{0pt}{0pt}} &
- \multicolumn{1}{|c}{ВВОДИМЫЕ ДАННЫЕ; ТИП АРГУМЕНТА}
- \\
- \hline
- \rule{0pt}{3ex}\verb| d| &десятичное целое; \verb|int *|. \\
- \verb| i| &целое; \verb|int *|. Целое может быть восьмеричным (с ведущим
- \verb|0|) или шестнадцатеричным (с ведущими \verb|0x| или \verb|0X|). \\
- \verb| o| &восьмеричное целое (с ведущим нулём или без него); \verb|int *|. \\
- \verb| u| &беззнаковое десятичное целое; \verb|unsigned int *|. \\
- \verb| x| &шестнадцатеричное целое (с ведущими \verb|0x| или \verb|0X| или без
- них); \verb|int *|. \\
- \verb| c| &литеры; \verb|char *|. Следующие литеры ввода (по умолчанию одна)
- размещаются в указанном месте. Обычный пропуск пробельных литер подавляется;
- чтобы прочесть очередную литеру, отличную от пробельной, используйте
- \verb|%1s|. \\
- \verb| s| &стринг литер (без обрамляющих кавычек); \verb|char *|, указывающий
- на массив литер, достаточный для стринга и завершающей литеры \verb|'\0'|,
- которая будет добавлена. \\
- \verb| e,f,g| &число с плавающей точкой, возможно, со знаком; обязательно
- присутствие либо десятичной точки‚ либо экспоненциальной части, а возможно, и
- обеих вместе; \verb|float *|. \\
- \rule[-1.5ex]{0pt}{0pt}\verb| %| &сам знак \verb|%|, никакое присваивание не
- выполняется. \\
- \hline
- \end{tabular*}
- \end{table}
- %
- % исправлена орфографические ошибки
- % в оригинале было ``шестнадцатиричным'' и ``шестнадцатиричное''
- %
- \noindent Функция \verb|scanf| читает литеры из стандартного входного потока,
- интерпретирует их согласно спецификациям стринга \verb|format| и рассылает
- результаты в свои остальные аргументы. Ар\-гу\-мент-фор\-мат мы опишем позже;
- другие аргументы, \emph{каждый из которых должен быть указателем}, определяют,
- где будут запоминаться должным образом преобразованные данные. Как и для
- \verb|printf|, в этом разделе даётся сводка наиболее полезных, но отнюдь не всех
- возможностей данной функции.
- Функция \verb|scanf| прекращает работу, когда оказывается, что исчерпался формат
- или вводимая величина не соответствует управляющей спецификации. В качестве
- результата \verb|scanf| возвращает количество успешно введённых элементов
- данных. По исчерпании файла она выдаёт \verb|EOF|. Существенно то, что значение
- \verb|EOF| не равно нулю, поскольку нуль \verb|scanf| выдаёт, когда вводимая
- литера не соответствует первой спецификации форматного стринга. Каждое очередное
- обращение к \verb|scanf| продолжает ввод с литеры, следующей сразу за последней
- обработанной.
- Существует также функция
- \index{библиотечная функция!sscanf@\texttt{sscanf}}%
- \verb|sscanf|, которая читает из стринга (а не из стандартного ввода).
- \begin{ShortCodeParWithCC}{\\\{\}}
- int sscanf(char *string, char *format, \textit{arg}\textsubscript{1},\
- \textit{arg}\textsubscript{2}, ...)
- \end{ShortCodeParWithCC}
- \noindent Функция \verb|sscanf| просматривает \verb|string| согласно формату
- \verb|format| и рассылает полученные значения в \textit{arg}$_{1}$,
- \textit{arg}$_{2}$ и т.д. Последние должны быть указателями.
- Формат обычно содержит спецификации, которые используются для управления
- преобразованиями ввода. В него могут входить следующие элементы:
- \begin{itemize}
- \item Пробелы или табуляции, которые игнорируются.
- \item Обычные литеры (исключая \verb|%|), которые, как ожидается, совпадут с
- очередными непробельными литерами входного потока.
- \item Спецификации преобразования, каждая из которых начинается со знака
- \verb|%| и завершается литерой-спецификатором типа преобразования. В
- промежутке между этими двумя литерами в любой спецификации могут
- располагаться, причём в том порядке, как они здесь указаны: знак \verb|*|
- (признак подавления присваивания); число, определяющее ширину поля; буква
- \verb|h|, \verb|l| или \verb|L|, указывающая на размер получаемого значения.
- \end{itemize}
- Спецификация преобразования управляет преобразованием следующего вводимого поля.
- Обычно результат помещается в переменную, на которую указывает соответствующий
- аргумент.
- \index{присваивание!подавленное, \texttt{scanf}}%
- \index{scanf, подавление присваивания в@\texttt{scanf}, подавление присваивания в}%
- Однако если в спецификации преобразования присутствует \verb|*|, то поле ввода
- пропускается и никакое присваивание не выполняется.
- \index{литеры!пробельные}%
- \index{пробельные литеры}%
- Поле ввода определяется как стринг без пробельных литер; оно простирается до
- следующей пробельной литеры или же ограничено шириной поля, если она задана.
- Поскольку литера новая-строка относится к пробельным литерам, это значит, что
- \verb|scanf| при чтении будет переходить с одной строки на другую. (Пробельными
- литерами являются литеры пробела, табуляции, новой-строки, возврата-каретки,
- вертикальной-табуляции и перевода-страницы.)
- Литера-спецификатор указывает, каким образом следует интерпретировать очередное
- поле ввода. Соответствующий аргумент должен быть указателем, как того требует
- механизм передачи параметров по значению, принятый в Си. Литеры-спецификаторы
- приведены в табл.~\ref{tab:scanf_chapt7}.
- Перед литерами-спецификаторами \verb|d|, \verb|i|, \verb|o|, \verb|u| и \verb|x|
- может стоять буква \verb|h|, указывающая на то, что соответствующий аргумент
- должен иметь тип \verb|short *| (а не \verb|int *|), или \verb|l| (латинская
- эль), указывающая на тип \verb|long *|. Аналогично, перед
- литерами-спецификаторами \verb|e|, \verb|f| и \verb|g| может стоять \verb|l|,
- указывающая, что тип аргумента -- \verb|double *| (а не \verb|float *|).
- \index{программа!калькулятор}%
- Чтобы построить первый пример, обратимся к программе калькулятора из
- гл.~\ref{chapt:functions_and_program_structure}, в которой организуем ввод с
- помощью функции \verb|scanf|:
- \begin{LongCodePar}
- #include <stdio.h>
- main() /* программа-калькулятор */
- {
- double sum, v;
- sum = 0;
- while (scanf("%lf", &v) == 1)
- printf("\t%.2f\n", sum += v);
- return 0;
- }
- \end{LongCodePar}
- Предположим, что нам нужно прочитать строки ввода, содержащие данные вида
- \begin{ShortCodePar}
- 25 дек 1988
- \end{ShortCodePar}
- \noindent Обращение к \verb|scanf| выглядит следующим образом:
- \begin{ShortCodePar}
- int day, year; /* день, год */
- char monthname[20]; /* название месяца */
- scanf("%d %s %d", &day, monthname, &year);
- \end{ShortCodePar}
- \noindent Знак \verb|&| перед \verb|monthname| не нужен, так как имя массива
- есть указатель.
- В стринге формата могут присутствовать литеры, не участвующие ни в одной из
- спецификаций; это значит, что эти литеры должны появиться на вводе. Так, мы
- могли бы читать даты вида \verb|mm/dd/yy| при помощи следующего обращения к
- \verb|scanf|:
- \begin{ShortCodePar}
- int day, month, year; /* день, месяц, год */
- scanf("%d/%d/%d", &day, &month, &year);
- \end{ShortCodePar}
- В своём формате функция \verb|scanf| игнорирует пробелы и табуляции. Кроме того,
- при поиске следующей порции ввода она пропускает во входном потоке все
- пробельные литеры (пробелы, табуляции, но\-вые-стро\-ки и т.д.). Воспринимать
- входной поток, не имеющий фиксированного формата, часто оказывается удобнее,
- если вводить всю строку целиком и для каждого отдельного случая подбирать
- подходящий вариант \verb|sscanf|. Предположим, например, что нам нужно читать
- строки с датами, записанными в любой из приведённых выше форм. Тогда мы могли бы
- написать:
- \begin{LongCodePar}
- while (getline(line, sizeof(line)) > 0) {
- if (sscanf(line, "%d %s %d", &day, monthname, &year)==3)
- printf("верно: %s\n", line); /* типа 25 дек 1988 */
- else if (sscanf(line, "%d/%d/%d", &month, &day, &year)==3)
- printf("верно: %s\n", line); /* типа mm/dd/yy */
- else
- printf("неверно: %s\n",line); /* неверн. форма даты */
- }
- \end{LongCodePar}
- %
- % для целей выравнивания
- % в предыдущем блоке кода
- % удалены несколько пробелов, что не повлияло на смысл кода
- %
- Обращения к \verb|scanf| могут перемежаться с вызовами других функций ввода.
- Любая функция ввода, вызванная после \verb|scanf|, продолжит чтение с первой ещё
- непрочитанной литеры.
- В завершение ещё раз напомним, что аргументы \verb|scanf| и \verb|sscanf|
- \emph{должны} быть указателями.
- Одна из самых распространённых ошибок состоит в том, что вместо того, чтобы
- написать
- \begin{ShortCodePar}
- scanf("%d", &n);
- \end{ShortCodePar}
- \noindent пишут
- \begin{ShortCodePar}
- scanf("%d", n);
- \end{ShortCodePar}
- \noindent Компилятор о подобной ошибке ничего не сообщает.
- \paragraph{Упражнение 7.4.} Напишите свою версию \verb|scanf| по аналогии с
- \verb|minprintf| из предыдущего раздела.
- \paragraph{Упражнение 7.5.} Перепишите основанную на постфиксной записи
- программу калькулятора из гл.~\ref{chapt:functions_and_program_structure} таким
- образом, чтобы для ввода и преобразования чисел она использовала \verb|scanf|
- и/или \verb|sscanf|.
- \section{Доступ к файлам}
- \index{файл!доступ к}%
- \index{файл!открытие}%
- Во всех предыдущих примерах мы имели дело со стандартным вводом и стандартным
- выводом, которые для программы автоматически предопределены операционной
- системой конкретной машины.
- \index{файл!добавляемый}%
- Следующий шаг -- научиться писать программы, которые имели бы доступ к файлам,
- заранее \emph{не} подсоединённым к программам.
- \index{программа!конкатенации файлов|(}%
- \index{программа!cat@\texttt{cat}|(}%
- \index{cat@\texttt{cat}}%
- Одна из программ, в которой возникает такая необходимость -- это программа
- \verb|cat|, объединяющая несколько именованных файлов и направляющая результат в
- стандартный вывод. Функция \verb|cat| часто применяется для выдачи файлов на
- экран, а также как универсальный <<коллектор>> файловой информации тех программ,
- которые не имеют возможности обратиться к файлу по имени. Например, команда
- \begin{ShortCodePar}
- cat x.c y.c
- \end{ShortCodePar}
- \noindent направит в стандартный вывод содержимое файлов \verb|x.c| и
- \verb|y.c| (и ничего более).
- Возникает вопрос: что надо сделать, чтобы именованные файлы можно было читать;
- иначе говоря, как связать внешние имена, придуманные пользователем, с
- инструкциями чтения данных?
- На этот счёт имеются простые правила. Для того чтобы можно было читать из файла
- или писать в файл, он должен быть предварительно \emph{открыт} при помощи
- \index{библиотечная функция!fopen@\texttt{fopen}}%
- библиотечной функции \verb|fopen|. Функция \verb|fopen| получает внешнее имя
- типа \verb|x.c| или \verb|y.c|, после чего осуществляет некоторые
- организационные действия и <<переговоры>> с операционной системой (технические
- детали которых здесь не рассматриваются) и возвращает указатель, используемый в
- дальнейшем для доступа к файлу.
- \index{указатель!файла}%
- \index{файл!указатель}%
- Этот указатель, называемый \emph{указателем файла}, ссылается на структуру,
- содержащую информацию о файле (адрес буфера, положение текущей литеры в буфере,
- открыт файл на чтение или на запись, были ли ошибки при работе с файлом и
- встретился ли конец файла).
- \index{FILE a@\texttt{FILE}}%
- Пользователю не нужно знать подробности, поскольку
- определения, полученные из \verb|<stdio.h>|‚ включают описание такой структуры,
- называемой \verb|FILE|. Единственное, что требуется для определения указателя
- файла, -- это задать декларации такого, например, вида:
- \begin{ShortCodePar}
- FILE *fp;
- FILE *fopen(char *name, char *mode);
- \end{ShortCodePar}
- \noindent Из этой записи следует, что \verb|fp| есть указатель на \verb|FILE|, а
- \verb|fopen| возвращает указатель на \verb|FILE|. Заметим, что \verb|FILE| есть
- имя типа, наподобие \verb|int|, а не тег структуры. Оно определено при помощи
- \verb|typedef|. (Детали того, как можно реализовать \verb|fopen| в системе UNIX,
- приводятся в разд.~\ref{sec:fopen_and_getc}.)
- Обращение к \verb|fopen| в программе может выглядеть следующим образом:
- \begin{ShortCodePar}
- fp = fopen(name, mode);
- \end{ShortCodePar}
- \noindent Первый аргумент -- стринг, содержащий имя файла.
- \index{файл!режим доступа}%
- Второй аргумент несёт информацию о \emph{режиме}. Это тоже стринг: в нём
- указывается, каким образом пользователь намерен использовать файл. Возможны
- следующие режимы: чтение (read -- \verb|"r"|), запись (write -- \verb|"w"|) и
- добавление (append -- \verb|"a"|), т.е. запись информации в конец уже
- существующего файла. В некоторых системах различаются текстовые и
- \index{бинарный поток}%
- \index{поток!бинарный}%
- бинарные файлы; в случае последних в стринг режима необходимо добавить букву
- \verb|"b"| (binary -- бинарный).
- \index{файл!создание}%
- Тот факт, что некий файл, которого раньше не было, открывается на запись или
- добавление, означает, что он создаётся (если такая процедура физически
- возможна). Открытие уже существующего файла на запись приводит к выбрасыванию
- его старого содержимого, в то время как при открытии файла на добавление его
- старое содержимое сохраняется. Попытка читать несуществующий файл является
- ошибкой. Могут иметь место и другие ошибки; например, ошибкой считается попытка
- чтения файла, который по статусу запрещено читать. При наличии любой ошибки
- \verb|fopen| возвращает \verb|NULL|. (Возможна более точная идентификация
- ошибки; детальная информация по этому поводу приводится в конце
- разд.~\ref{apx:stdlib:sec:input-output-stdio.h} приложения~\ref{apx:stdlib}.)
- Следующее, что нам необходимо знать, -- это как читать из файла или писать в
- файл, коль скоро он открыт. Существует несколько способов сделать это, из
- которых самый простой состоит в том, чтобы воспользоваться функциями
- \index{библиотечная функция!getc@\texttt{getc}}%
- \verb|getc| и
- \index{библиотечная функция!putc@\texttt{putc}}%
- \verb|putc|. Функция \verb|getc| возвращает следующую литеру из файла; ей
- необходимо сообщить указатель файла, чтобы она знала откуда брать литеру.
- \begin{ShortCodePar}
- int getc(FILE *fp)
- \end{ShortCodePar}
- \noindent Функция \verb|getc| возвращает следующую литеру из потока, на который
- ссылаются при помощи \verb|*fp|; в случае исчерпания файла или ошибки она
- возвращает \verb|EOF|.
- \begin{ShortCodePar}
- int putc(int c, FILE *fp)
- \end{ShortCodePar}
- \noindent Функция \verb|putc| пишет литеру \verb|c| в файл \verb|fp| и
- возвращает записанную литеру или \verb|EOF|, в случае ошибки. Аналогично
- \index{библиотечная функция!getchar@\texttt{getchar}}%
- \verb|getchar| и \verb|putchar|‚ программы \verb|getc| и \verb|putc| могут быть
- реализованы в виде макросов, а не функций.
- При запуске Си-программы операционная система всегда открывает три файла и
- обеспечивает три файловые ссылки на них.
- \index{стандартный!ввод}%
- \index{стандартный!вывод}%
- Этими файлами являются: стандартный ввод, стандартный вывод и стандартный файл
- ошибок;
- \index{stderr@\texttt{stderr}}%
- \index{stdin@\texttt{stdin}}%
- \index{stdout@\texttt{stdout}}%
- соответствующие им указатели называются \verb|stdin|, \verb|stdout| и
- \verb|stderr|; они описаны в \verb|<stdio.h>|. Обычно \verb|stdin| соотнесён с
- клавиатурой, а \verb|stdout| и \verb|stderr| -- с экраном.
- \index{ввод-вывод!перенаправление}%
- Однако \verb|stdin| и \verb|stdout| можно связать с файлами или, используя
- механизм <<трубопровода>>, соединить напрямую с другими программами, как это
- описывалось в разд.~\ref{chapt:input-output:sec:standard_input-output}.
- При помощи \verb|getc|, \verb|putc|, \verb|stdin|, \verb|stdout| функции
- \verb|getchar| и \verb|putchar| теперь можно определить следующим образом:
- \begin{ShortCodePar}
- #define getchar() getc(stdin)
- #define putchar(c) putc((c), stdout)
- \end{ShortCodePar}
- Форматный ввод-вывод файлов можно построить на функциях
- \index{библиотечная функция!fscanf@\texttt{fscanf}}%
- \verb|fscanf| и
- \index{библиотечная функция!fprintf@\texttt{fprintf}}%
- \verb|fprintf|. Они идентичны \verb|scanf| и \verb|printf| с той лишь разницей,
- что первым их аргументом является указатель, ссылающийся на файл, для которого
- осуществляется ввод-вывод, формат же указывается вторым аргументом.
- \begin{ShortCodePar}
- int fscanf(FILE *fp, char *format, ...)
- int fprintf(FILE *fp, char *format, ...)
- \end{ShortCodePar}
- Вот теперь мы располагаем теми сведениями, которые достаточны для написания
- программы \verb|cat|, предназначенной для конкатенации (последовательного
- соединения) файлов. Предлагаемая версия функции \verb|cat|, как оказалось,
- удобна для многих программ. Если в командной строке присутствуют аргументы, они
- рассматриваются как имена последовательно обрабатываемых файлов. Если аргументов
- нет, то обработке подвергается стандартный ввод.
- \index{функция!filecopy@\texttt{filecopy}}%
- \begin{LongCodePar}
- #include <stdio.h>
- /* cat: конкатенация файлов, версия 1 */
- main(int argc, char *argv[])
- {
- FILE *fp;
- void filecopy(FILE *, FILE *);
- if (argc == 1) /* нет арг-тов; копир-ся станд. ввод */
- filecopy(stdin, stdout);
- else
- while (--argc > 0)
- if ((fp = fopen(*++argv, "r")) == NULL) {
- printf("cat: не могу открыть файл %s\n",
- *argv);
- return 1;
- } else {
- filecopy(fp, stdout);
- fclose(fp);
- }
- return 0;
- }
- /* filecopy: копирует файл ifp в файл ofp */
- void filecopy(FILE *ifp, FILE *ofp)
- {
- int c;
- while ((c = getc(ifp)) != EOF)
- putc(c, ofp);
- }
- \end{LongCodePar}
- \noindent Файловые указатели \verb|stdin| и \verb|stdout| представляют собой
- объекты типа \verb|FILE *|. Это константы, а \emph{не} переменные,
- следовательно, им нельзя ничего присваивать.
- \index{библиотечная функция!fclose@\texttt{fclose}}%
- Функция
- \begin{ShortCodePar}
- int fclose(FILE *fp)
- \end{ShortCodePar}
- \noindent -- обратная по отношению к \verb|fopen|; она разрывает связь между
- файловым указателем и внешним именем (которая раньше была установлена при помощи
- \verb|fopen|), освобождая тем самым этот указатель для других файлов. Так как в
- большинстве операционных систем количество одновременно открытых одной
- программой файлов ограничено, то файловые указатели, если они больше не нужны,
- лучше освобождать, как это и делается в программе \verb|cat|. Есть ещё одна
- причина применить \verb|fclose| к файлу вывода, -- это необходимость
- <<опорожнить>> буфер, в котором \verb|putc| накопила, предназначенные для вывода
- данные. При нормальном завершении работы программы для каждого открытого файла
- \verb|fclose| вызывается автоматически. (Вы можете закрыть \verb|stdin| и
- \verb|stdout|, если они вам не нужны. Воспользовавшись библиотечной функцией
- \verb|freopen| их можно создать заново.)
- \index{программа!cat@\texttt{cat}|)}%
- \index{программа!конкатенации файлов|)}%
- %
- % Запятая в
- % "в котором putc накопила, предназначенные для вывода данные."
- % это особенность оригинала. Нужна ли там запятая - сомнительно, но не
- % исключено. Если - да, то почему нет запятой перед словом "данные"?
- %
- \section{Управление ошибками (\texorpdfstring{\protect\Verb|stderr|}{stderr} и
- \texorpdfstring{\protect\Verb|exit|}{exit})}
- \index{библиотечная функция!exit@\texttt{exit}}%
- \index{завершение программы|(}%
- Обработку ошибок в \verb|cat| нельзя признать идеальной. Беда в том, что если
- файл по какой-либо причине недоступен, сообщение об этом мы получим по окончании
- конкатенируемого вывода. Это нас устроило бы, если бы
- \index{вывод!на экран}%
- вывод отправлялся только на экран, а не в файл или другой программе, напрямую по
- <<трубопроводу>>.
- %
- % исправлена опечатка
- % в оригинале было ``Обработка''
- %
- \index{ввод-вывод!ошибки}%
- \index{stderr@\texttt{stderr}}%
- Чтобы лучше справиться с этой проблемой, программе помимо стандартного вывода
- \verb|stdout| придаётся ещё один выходной поток, называемый \verb|stderr|.
- \index{вывод!на экран}%
- Вывод в \verb|stderr| обычно отправляется на экран, даже если вывод
- \verb|stdout| перенаправлен в другое место.
- Перепишем \verb|cat| так, чтобы сообщения об ошибках отправлялись в
- \verb|stderr|.
- \begin{LongCodePar}
- #include <stdio.h>
- /* cat: конкатенация файлов, версия 2 */
- main(int argc, char *argv[])
- {
- FILE *fp;
- void filecopy(FILE *, FILE *);
- char *prog = argv[0]; /* имя программы */
- if (argc == 1) /* нет арг-тов; копир-ся станд. ввод */
- filecopy(stdin, stdout);
- else
- while (--argc > 0)
- if ((fp = fopen(*++argv, "r")) == NULL) {
- fprintf(stderr, "%s: не могу откр.файл %s\n",
- prog, *argv);
- exit(1);
- } else {
- filecopy(fp, stdout);
- fclose(fp);
- }
- if (ferror(stdout)) {
- fprintf(stderr, "%s: ошибка записи в stdout\n", prog);
- exit(2);
- }
- exit(0);
- }
- \end{LongCodePar}
- Программа сигнализирует об ошибках двумя способами. Первый -- сообщение об
- ошибке при помощи \verb|fprintf| посылается в \verb|stderr| с тем, чтобы оно
- попало на экран, а не оказалось в <<трубопроводе>> или в другом файле вывода.
- Имя программы, хранящееся в \verb|argv[0]|, мы включили в сообщение, чтобы в
- случаях, когда данная программа работает совместно с другими, был ясен источник
- ошибки.
- \index{ввод-вывод!ошибки}%
- \index{main@\texttt{main}!возврат из}%
- Второй способ указать на ошибку -- обратиться к библиотечной функции
- \verb|exit|, завершающей работу программы. Аргумент функции \verb|exit| доступен
- некоторому процессу, вызвавшему данный процесс. А следовательно, успешное или
- ошибочное завершение программы можно проконтролировать с помощью некоей
- программы, которая рассматривает эту программу в качестве подчинённого процесса.
- По общей договорённости возврат нуля сигнализирует о том, что работа прошла
- нормально, в то время как ненулевые значения обычно говорят об ошибках. Чтобы
- опустошить буфера, накопившие информацию для всех открытых файлов вывода,
- функция \verb|exit| вызывает \verb|fclose|.
- \index{return@\texttt{return}!из \texttt{main}}%
- Инструкция главной программы \verb|return |\textit{\Verb|выр|} эквивалентна
- обращению к функции \verb|exit(|\textit{\Verb|выр|}\verb|)|. Последняя запись
- (при помощи \verb|exit|) имеет то преимущество, что она пригодна для выхода и из
- других функций, и, кроме того, её легко обнаружить при помощи программы
- контекстного поиска, похожей на ту, которую мы рассматривали в
- гл.~\ref{chapt:pointers_and_arrays}.
- Функция \verb|ferror| выдаёт ненулевое значение, если в файле \verb|fp| была
- обнаружена ошибка.
- \begin{ShortCodePar}
- int ferror(FILE *fp)
- \end{ShortCodePar}
- \noindent Хотя при выводе редко возникают ошибки, всё же они встречаются
- (например, оказался переполненным диск); поэтому в программах широкого
- пользования они должны тщательно контролироваться.
- \index{библиотечная функция!feof@\texttt{feof}}%
- Функция \verb|feof(FILE *)| аналогична функции \verb|ferror|; она возвращает
- ненулевое значение, если встретился конец указанного в аргументе файла.
- \begin{ShortCodePar}
- int feof(FILE *fp)
- \end{ShortCodePar}
- \noindent В наших небольших иллюстративных программах мы не заботились о выдаче
- статуса выхода, т.е. выдаче некоторого числа, характеризующего состояние
- программы в момент завершения: работа закончилась нормально или прервана из-за
- ошибки? Если работа прервана в результате ошибки, то какой? Любая серьёзная
- программа должна выдавать статус выхода.
- \index{завершение программы|)}%
- \section{Ввод-вывод строк}
- В стандартной библиотеке имеется программа ввода
- \index{библиотечная функция!fgets@\texttt{fgets}}%
- \verb|fgets|, аналогичная программе \verb|getline|, которой мы пользовались в
- предыдущих главах.
- \begin{ShortCodePar}
- char *fgets(char *line, int maxline, FILE *fp)
- \end{ShortCodePar}
- \noindent Функция \verb|fgets| читает следующую строку ввода (включая и литеру
- новая-строка) из файла \verb|fp| в массив литер \verb|line|, причём она может
- прочитать не более \verb|maxline-1| литер. Переписанная строка дополняется
- литерой \verb|'\0'|. Обычно \verb|fgets| возвращает \verb|line|, а по исчерпании
- файла или в случае ошибки -- \verb|NULL|. (Наша \verb|getline| возвращала длину
- строки, которой мы потом пользовались, и нуль по концу файла.)
- Функция вывода
- \index{библиотечная функция!fputs@\texttt{fputs}}%
- \verb|fputs| пишет стринг (который может и не заканчиваться литерой
- новая-строка) в файл.
- \begin{ShortCodePar}
- int fputs(char *line, FILE *fp)
- \end{ShortCodePar}
- \noindent Эта функция возвращает \verb|EOF|, если возникла ошибка, и нуль в
- противном случае.
- Библиотечные функции
- \index{библиотечная функция!gets@\texttt{gets}}%
- \verb|gets| и
- \index{библиотечная функция!puts@\texttt{puts}}%
- \verb|puts| подобны функциям \verb|fgets| и \verb|fputs|. Отличаются они тем,
- что оперируют только стандартными файлами \verb|stdin| и \verb|stdout|, и, кроме
- того, \verb|gets| выбрасывает последнюю литеру \verb|'\n'|‚ а \verb|puts| её
- добавляет.
- \index{функция!fgets@\texttt{fgets}}%
- \index{функция!fputs@\texttt{fputs}}%
- Чтобы показать, что ничего особенного в функциях типа \verb|fgets| и
- \verb|fputs| нет, мы приводим их здесь в том виде, в каком они существуют в
- стандартной библиотеке на нашей системе.
- \begin{LongCodePar}
- /* fgets: получает не более n литер из iop */
- char *fgets(char *s, int n, FILE *iop)
- {
- register int c;
- register char *cs;
- cs = s;
- while (--n > 0 && (c = getc(iop)) != EOF)
- if ((*cs++ = c) == '\n')
- break;
- *cs = '\0';
- return (c == EOF && cs == s) ? NULL : s;
- }
- /* fputs: посылает стринг s в файл iop */
- int fputs(char *s, FILE *iop)
- {
- int c;
- while (c = *s++)
- putc(c, iop);
- return ferror(iop) ? EOF : 0;
- }
- \end{LongCodePar}
- \paragraph{Упражнение 7.6.} Напишите программу, сравнивающую два файла и
- печатающую первую строку, в которой они различаются.
- \paragraph{Упражнение 7.7.} Модифицируйте программу поиска по образцу из
- гл.~\ref{chapt:pointers_and_arrays} таким образом, чтобы она брала текст из
- множества именованных файлов, а если имён файлов в аргументах нет, то -- из
- стандартного ввода. Следует ли печатать имя файла, в котором найдена подходящая
- строка?
- \paragraph{Упражнение 7.8.} Напишите программу, печатающую несколько файлов.
- Каждый файл должен начинаться с новой страницы, предваряться заголовком и иметь
- свою нумерацию страниц.
- \section{Другие библиотечные функции}
- В стандартной библиотеке представлен широкий спектр различных функций. Настоящий
- раздел содержит краткий обзор наиболее полезных из них. Более подробно эти и
- другие функции описаны в приложении~\ref{apx:stdlib}.
- \subsection{Операции со стрингами}
- Мы уже упоминали функции \verb|strlen|, \verb|strcpy|, \verb|strcat| и
- \verb|strcmp|, описание которых даны в \verb|<string.h>|. Далее, до конца
- подраздела, предполагается, что \verb|s| и \verb|t| имеют тип \verb|char *|,
- \verb|c| и \verb|n| -- тип \verb|int|.
- \begin{LongRtAlTab2cols}{\codeIndent}{0pt}{\Verb|strncat(s,t,n)|}
- \verb|strcat(s,t)|%
- \index{библиотечная функция!strcat@\texttt{strcat}}%
- &конкатенирует \verb|t| в конец \verb|s| \\
- \verb|strncat(s,t,n)|%
- \index{библиотечная функция!strncat@\texttt{strncat}}%
- &конкатенирует \verb|n| литер \verb|t| в конец \verb|s| \\
- \verb|strcmp(s,t)|%
- \index{библиотечная функция!strcmp@\texttt{strcmp}}%
- &возвращает отрицательное число, нуль или положительное число
- для \verb|s < t|, \verb|s == t| или \verb|s > t| соответственно \\
- \verb|strncmp(s,t,n)|%
- \index{библиотечная функция!strncmp@\texttt{strncmp}}%
- &делает то же, что и \verb|strcmp|‚ но количество
- сравниваемых литер не может превышать \verb|n| \\
- \verb|strcpy(s,t)|%
- \index{библиотечная функция!strcpy@\texttt{strcpy}}%
- &копирует \verb|t| в \verb|s| \\
- \verb|strncpy(s,t,n)|%
- \index{библиотечная функция!strncpy@\texttt{strncpy}}%
- &копирует не более \verb|n| литер \verb|t| в \verb|s| \\
- \verb|strlen(s)|%
- \index{библиотечная функция!strlen@\texttt{strlen}}%
- \index{стринг!длина}%
- &возвращает длину \verb|s| \\
- \verb|strchr(s,c)|%
- \index{библиотечная функция!strchr@\texttt{strchr}}%
- &возвращает указатель на первое появление литеры \verb|c| в
- \verb|s| или, если \verb|c| нет в \verb|s|, \verb|NULL| \\
- \verb|strrchr(s,c)|%
- \index{библиотечная функция!strrchr@\texttt{strrchr}}%
- &возвращает указатель на последнее появление литеры \verb|c|
- в \verb|s| или, если \verb|c| нет в \verb|s|, \verb|NULL|
- \end{LongRtAlTab2cols}
- \subsection{Анализ класса литер и преобразование литер}
- \index{функции проверки литер}%
- Несколько функций из библиотеки \verb|<ctype.h>| выполняют проверки и
- преобразование литер. Далее, до конца подраздела, переменная \verb|c| -- это
- переменная типа \verb|int|, которая может быть представлена значением
- \verb|unsigned char| или \verb|EOF|. Функции возвращают значения типа
- \verb|int|.
- \begin{LongRtAlTab2cols}{\codeIndent}{0pt}{\Verb|isalpha(c)|}
- \verb|isalpha(c)|%
- \index{библиотечная функция!isalpha@\texttt{isalpha}}%
- &не нуль, если \verb|c| -- буква; $0$ в противном случае \\
- \verb|isupper(c)|%
- \index{библиотечная функция!isupper@\texttt{isupper}}%
- &не нуль, если \verb|c| -- буква верхнего регистра; $0$ в противном случае \\
- \verb|islower(c)|%
- \index{библиотечная функция!islower@\texttt{islower}}%
- &не нуль, если \verb|c| -- буква нижнего регистра; $0$ в противном случае \\
- \verb|isdigit(c)|%
- \index{библиотечная функция!isdigit@\texttt{isdigit}}%
- &не нуль, если \verb|c| -- цифра; $0$ в противном случае \\
- \verb|isalnum(c)|%
- \index{библиотечная функция!isalnum@\texttt{isalnum}}%
- &не нуль, если \verb|isalpha(c)|, или \verb|isdigit(c)| истинны; $0$ в противном
- случае \\
- \verb|isspace(c)|%
- \index{библиотечная функция!isspace@\texttt{isspace}}%
- \index{литеры!пробельные}%
- \index{пробельные литеры}%
- &не нуль, если \verb|c| -- литера пробела, табуляции,
- новой-строки, возврата-каретки, перевода-страницы, вертикальной-табуляции \\
- \verb|toupper(c)|%
- \index{библиотечная функция!toupper@\texttt{toupper}}%
- &возвращает \verb|c|, приведённую к верхнему регистру \\
- \verb|tolower(c)|%
- \index{библиотечная функция!tolower@\texttt{tolower}}%
- &возвращает \verb|c|, приведённую к нижнему регистру
- \end{LongRtAlTab2cols}
- %
- % в оригинале слева внизу страницы надпись ``6. Заказ № 13''
- %
- \subsection{Функция \texorpdfstring{\protect\Verb|ungetc|}{ungetc}}
- В стандартной библиотеке содержится более ограниченная версия функции
- \verb|ungetch| по сравнению с той, которую мы написали в
- гл.~\ref{chapt:functions_and_program_structure}. Называется она
- \index{библиотечная функция!ungetc@\texttt{ungetc}}%
- \verb|ungetc|. Эта функция, имеющая прототип
- %
- % исправлена опечатка
- % в оригинале ``ugetch''
- %
- \begin{ShortCodePar}
- int ungetc(int c, FILE *fp)
- \end{ShortCodePar}
- \noindent отправляет литеру \verb|c| назад в файл \verb|fp| и возвращает
- \verb|c| или \verb|EOF|, в случае ошибки. Для каждого файла гарантирован возврат
- не более одной литеры. Функцию \verb|ungetc| можно использовать совместно с
- любой из функций ввода типа \verb|scanf|‚ \verb|getc|, \verb|getchar| и т.д.
- \subsection{Исполнение команд операционной системы}
- Функция
- \index{библиотечная функция!system@\texttt{system}}%
- \verb|system(char *s)| выполняет команду системы, содержащуюся в стринге
- \verb|s|, и затем возвращается к выполнению текущей программы. Содержимое
- \verb|s|, строго говоря, зависит от конкретной операционной системы. Рассмотрим
- простой пример: в системе UNIX инструкция
- \begin{ShortCodePar}
- system("date");
- \end{ShortCodePar}
- \noindent вызовет программу \verb|date|, которая направит дату и время в
- стандартный вывод. Функция возвращает зависящий от системы статус выполненной
- команды. В системе UNIX возвращаемый статус -- это значение, переданное
- функцией \verb|exit|.
- \subsection{Управление памятью}
- Функции
- \index{библиотечная функция!malloc@\texttt{malloc}}%
- \verb|malloc| и
- \index{библиотечная функция!calloc@\texttt{calloc}}%
- \verb|calloc| получают динамически запрашиваемые ими области памяти. Функция
- \verb|malloc| с прототипом
- \begin{ShortCodePar}
- void *malloc(size_t n)
- \end{ShortCodePar}
- \noindent возвращает указатель на \verb|n| байт неинициализированной памяти или
- \verb|NULL|, если запрос удовлетворить нельзя. Функция \verb|calloc| с
- прототипом
- \begin{ShortCodePar}
- void *calloc(size_t n, size_t size)
- \end{ShortCodePar}
- \noindent возвращает указатель на область, достаточную для хранения массива из
- \verb|n| объектов указанного размера \verb|(size)|, или \verb|NULL|, если запрос
- не удаётся удовлетворить. Выделенная память обнуляется.
- Указатель, возвращаемый функциями \verb|malloc| и \verb|calloc|, будет выдан с
- учётом
- \index{выравнивание!ограничения по}%
- выравнивания, выполненного согласно указанному типу объекта.
- \index{оператор!приведения к типу}%
- Тем не менее к нему должна быть применена операция приведения к соответствующему
- типу, как это сделано в следующем фрагменте программы:
- \begin{ShortCodePar}
- int *ip;
- ip = (int *) calloc(n, sizeof(int));
- \end{ShortCodePar}
- %
- % исправлена опечатка
- % в оригинале ``siseof''
- %
- Функция \verb|free(p)| освобождает область памяти, на которую указывает
- \verb|p|, -- указатель, первоначально полученный с помощью \verb|malloc| или
- \verb|calloc|. Никаких ограничений на порядок, в котором будет освобождаться
- память, нет, но ужасной ошибкой считается освобождение тех областей, которые не
- были получены при помощи \verb|calloc| или \verb|malloc|.
- Нельзя также использовать те области памяти, которые уже освобождены. Следующий
- пример демонстрирует типичную ошибку в цикле, освобождающем элементы списка.
- \begin{ShortCodePar}
- for (p = head; p != NULL; p = p->next) /* НЕВЕРНО */
- free(p);
- \end{ShortCodePar}
- \noindent Правильным будет, если вы до освобождения сохраните то, что вам
- потребуется, как в следующем цикле:
- \begin{ShortCodePar}
- for (p = head; p != NULL; p = q) {
- q = p->next;
- free(p);
- }
- \end{ShortCodePar}
- \noindent В разд.~\ref{sec:memory_allocator} мы рассмотрим реализацию программы
- управления памятью типа \verb|malloc|, позволяющую освобождать выделенные блоки
- памяти в любой последовательности.
- \subsection{Математические функции}
- В \verb|<math.h>| описано более двадцати математических функций. Здесь же
- приведены наиболее употребительные. Каждая из них имеет один или два аргумента
- типа \verb|double| и возвращает результат также типа \verb|double|.
- \begin{LongRtAlTab2cols}{\codeIndent}{0pt}
- {\Verb|atan2(|\textit{\Verb|y|}\Verb|,|\textit{\Verb|x|}\Verb|)|}
- \verb|sin(|\textit{\Verb|x|}\verb|)|%
- \index{библиотечная функция!sin@\texttt{sin}}%
- &синус $x$, $x$ в радианах\\
- \verb|cos(|\textit{\Verb|x|}\verb|)|%
- \index{библиотечная функция!cos@\texttt{cos}}%
- &косинус $x$, $x$ в радианах \\
- \verb|atan2(|\textit{\Verb|y|}\verb|,|\textit{\Verb|x|}\verb|)|%
- \index{библиотечная функция!atan, atan2@\texttt{atan}, \texttt{atan2}}%
- &арктангенс $y/x$, $y/x$ в радианах \\
- \verb|exp(|\textit{\Verb|x|}\verb|)|%
- \index{библиотечная функция!exp@\texttt{exp}}%
- \index{экспоненциальная функция}%
- &экспоненциальная функция $\mathrm{e}^x$ \\
- \verb|log(|\textit{\Verb|x|}\verb|)|%
- \index{библиотечная функция!log, log10@\texttt{log}, \texttt{log10}}%
- &натуральный (по основанию $\mathrm{e}$) логарифм $x$ ($x>0$) \\
- \verb|log10(|\textit{\Verb|x|}\verb|)|%
- \index{библиотечная функция!log, log10@\texttt{log}, \texttt{log10}}%
- &обычный (по основанию $10$) логарифм $x$ ($x>0$) \\
- \verb|pow(|\textit{\Verb|x|}\verb|,|\textit{\Verb|y|}\verb|)|%
- \index{библиотечная функция!pow@\texttt{pow}}%
- &$x^y$ \\
- \verb|sqrt(|\textit{\Verb|x|}\verb|)|%
- \index{библиотечная функция!sqrt@\texttt{sqrt}}%
- &корень квадратный $x$ ($x\geqslant0$) \\
- \verb|fabs(|\textit{\Verb|x|}\verb|)|%
- \index{библиотечная функция!fabs@\texttt{fabs}}%
- &абсолютное значение $x$
- \end{LongRtAlTab2cols}
- \subsection{Генератор случайных чисел}
- Функция
- \index{библиотечная функция!rand@\texttt{rand}}%
- \verb|rand()| вычисляет последовательность псевдослучайных целых в диапазоне от
- нуля до значения, заданного
- \index{RAND{\_}MAX@\texttt{RAND{\_}MAX}}%
- именованной константой \verb|RAND_MAX|‚ которая
- определена в \verb|<stdlib.h>|. Привести случайные числа к значениям с плавающей
- точкой, б\'{o}льшим или равным $0$ и меньшим $1$, можно по формуле
- \begin{ShortCodePar}
- #define frand() ((double) rand() / (RAND_MAX+1.0))
- \end{ShortCodePar}
- \noindent (Если в вашей библиотеке уже есть функция для получения случайных
- чисел с плавающей точкой, вполне возможно, что её статистические характеристики
- лучше указанной.)
- Функция
- \index{библиотечная функция!srand@\texttt{srand}}%
- \verb|srand(unsigned)| осуществляет <<начальную затравку>> для \verb|rand|.
- Реализации \verb|rand| и \verb|srand|, предлагаемые стандартом и, следовательно,
- переносимые на различные машины, рассмотрены в
- разд.~\ref{chapt:types-operators-expressions:sec:typecasting}.
- \paragraph{Упражнение 7.9.} Реализуя функции типа \verb|isupper|, можно либо
- экономить память, либо время. Напишите оба варианта функции.
- %
- % в оригинале в левом нижнем углу страницы ``6*''
- %
|