\chapter{Ввод-вывод} \label{chapt:input-output} \index{ввод-вывод}% Возможности ввода-вывода не являются частью самого языка Си, поэтому мы подробно и не рассматривали их до сих пор. Между тем реальные программы взаимодействуют со своим окружением гораздо более сложным способом, чем те, которые были затронуты ранее. В этой главе мы опишем стандартную библиотеку, набор функций, обеспечивающих ввод-вывод, работу со стрингами, управление памятью, стандартные математические функции и разного рода сервисные Си-программы. Но особое внимание уделим вводу-выводу. \index{переносимость}% Библиотечные функции ввода-вывода точно определяются стандартом ANSI, так что они совместимы на любых установках, где поддерживается Си. Программы, которые в своём взаимодействии с системным окружением не выходят за рамки возможностей стандартной библиотеки, можно без изменений переносить с одной машины на другую. Свойства библиотечных функций специфицированы в более чем дюжине головных файлов; \index{файл!головной!@\texttt{}}% \index{stdio.h@\texttt{}}% вам уже встречались некоторые из них, в том числе \verb||, \verb|| и \verb||. Мы не рассматриваем здесь всю библиотеку, так как нас больше интересует написание Си-программ, чем использование библиотечных функций. Стандартная библиотека подробно описана в приложении~\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{файл!головной!@\texttt{}}% \index{EOF@\texttt{EOF}}% \index{stdio.h@\texttt{}}% Именованная константа \verb|EOF| (аббревиатура от end of file -- конец файла) определена в \verb||. Обычно значение \verb|EOF| равно $-1$, но, чтобы не зависеть от конкретного значения этой константы, ссылаться на неё следует по имени (\verb|EOF|). \index{ввод-вывод!перенаправление}% Во многих системах клавиатуру можно заменить файлом, перенаправив ввод при помощи значка \verb|<|. Так, если программа \verb|prog| использует \verb|getchar|, то командная строка \begin{ShortCodePar} prog > механизм. В некоторых системах командная строка \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{файл!головной!@\texttt{}}% \index{include@\texttt{{\#}include}}% \index{stdio.h@\texttt{}}% Любой исходный Си-файл, использующий хотя бы одну функцию библиотеки ввода-вывода, должен содержать в себе строку \begin{ShortCodePar} #include \end{ShortCodePar} \noindent причём она должна быть расположена до первого обращения к вводу-выводу. Если имя головного файла заключено в угловые скобки \verb|<| и \verb|>|, это значит, что поиск головного файла ведётся в стандартном месте (например, в системе UNIX это обычно директория \verb|/usr/include|). Многие программы читают только из одного входного потока и пишут только в один выходной поток. Для организации ввода-вывода таким программам вполне хватит функций \verb|getchar|, \verb|putchar| и \verb|printf|, а для начального обучения ознакомления с этими функциями уж точно достаточно. В частности, перечисленных функций достаточно, когда требуется вывод одной программы соединить с вводом следующей. \index{программа!перевода в нижний регистр}% В качестве примера рассмотрим программу \verb|lower|, переводящую свой ввод на нижний регистр: \begin{LongCodePar} #include #include main() /* lower: переводит ввод на нижний регистр */ { int c; while ((c = getchar()) != EOF) putchar(tolower(c)); return 0; } \end{LongCodePar} Функция \verb|tolower| определена в \verb||. Она переводит буквы верхнего регистра в буквы нижнего регистра, а остальные литеры возвращает без изменений. Как мы уже упоминали, <<функции>> типа \verb|getchar| и \verb|putchar| из библиотеки \verb|| и функция \verb|tolower| из библиотеки \verb|| часто реализуют в виде макросов, чтобы исключить накладные расходы от вызова функции на каждую отдельную литеру. В разд.~\ref{sec:fopen_and_getc} мы покажем, как это делается. Независимо от того, как на той или иной машине реализованы функции библиотеки \verb||‚ использующие их программы могут ничего не знать о кодировке литер. \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{файл!головной!@\texttt{}}% Стандартный головной файл \verb|| содержит набор макроопределений, которые определяют, как шагать по списку аргументов. Наполнение этого головного файла может изменяться от машины к машине, но представленный им интерфейс везде одинаков. \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| (от <> -- указатель на аргумент). Макрос \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 /* 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 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||‚ включают описание такой структуры, называемой \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||. Обычно \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 /* 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 /* 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||. Далее, до конца подраздела, предполагается, что \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|| выполняют проверки и преобразование литер. Далее, до конца подраздела, переменная \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|| описано более двадцати математических функций. Здесь же приведены наиболее употребительные. Каждая из них имеет один или два аргумента типа \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||. Привести случайные числа к значениям с плавающей точкой, б\'{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*'' %