\section{Базовый ввод-вывод} \subsection{Форматированный вывод} Общение с пользователем на чистом С происходит через консоль. Для того, чтобы выводить какую-либо информацию для чтения пользователем - используется функция \code{printf();} предназначенная для форматированного вывода некоторого текста в консоль. Функция описана в заголовке \code{stdio.h}, поэтому мы и включили данный заголовок в нашу программу. Какого рода форматирование применяется при выводе строк в консоль? Существуют два основных инструмента придания выводу необходимого вида: экранированные последовательности (escape sequences) и заполнители (placeholders). \paragraph{Экранированная последовательность} - это буква или символ, написанные после знака обратного слэша (\code{\char`\\}), которые при выполнении программы будут на что-то заменены. Самые часто используемые это: \begin{itemize} \item \code{\char`\\'} - одинарная кавычка; \item \code{\char`\\"} - двойная кавычка; \item \code{\char`\\?} - вопросительный знак; \item \code{\char`\\\char`\\} - обратный слэш; \item \code{\char`\\0} - нулевой символ; \item \code{\char`\\b} - забой (backspace); \item \code{\char`\\f} - перевод страницы - новая страница; \item \code{\char`\\n} - перевод строки - новая строка; \item \code{\char`\\r} - возврат каретки; \item \code{\char`\\t} - горизонтальная табуляция; \item \code{\char`\\v} - вертикальная табуляция; \item \code{\char`\\nnn} - произвольное восьмеричное значение; \item \code{\char`\\xnn} - произвольное шестнадцатеричное значение; \item \code{\char`\\unnnn} - произвольное Юникод-значение. \end{itemize} Чтобы убедиться, что это правильно работает, выведем еще одну строку (в дополнение к коду со страницы \hyperref[code:firstprogram]{\pageref{code:firstprogram}}) с надписью <<Это новая строка>> на следующую строку нашей консоли. Также добавим к ещё одной строке символ табуляции чтобы увидеть как он работает. И сразу рассмотрим экранированную последовательность \code{\char`\\\char`\\} она делает не что иное, как добавляет символ обратного слэша в наш текст. Аналогичным образом работают и другие символьные экранирования. Это нужно, чтобы компилятор мог отличить, например, символ двойных кавычек в строке, написанной программистом, от символа двойных кавычек, завершающего строку в коде программы. Обратите внимание, что, не поставив в конец строки последовательность \code{\char`\\n}, мы заставим компилятор постоянно писать текст на одной строке, не переходя на новую. И наконец, \code{\char`\\0} сообщает компилятору, что строка закончилась. Даже если у нас есть еще какие-то символы до закрывающих кавычек, компилятор их просто проигнорирует. Добавим в код программы ещё пару строк, таким образом тело функции \code{main} должно принять следующий вид: \begin{figure}[h!] \begin{lstlisting}[language=C,style=CCodeStyle] printf("Hello, world!\n"); printf("This is a new row"); printf("This is \"\t\"tab\n"); printf("This is \\ symbol\n"); printf("This is a terminant \0 it ends a string"); \end{lstlisting} \end{figure} Запустив программу, мы можем убедиться, что всё работает так, как мы описали в тексте: сначала идёт приветствие миру, на новой строке сообщение о том, что это новая строка, далее на той же строке (ведь мы не переходили на новую) демонстрация пробела и символа табуляции. На последних двух строках происходит демонстрация обратного слэша и терминанта, видно, что остаток строки не был выведен на экран. \begin{figure}[h!] \begin{verbatim} $ ./program Hello, World! This is a new rowThis is " "tab This is \ symbol This is terminant $ \end{verbatim} \end{figure} \paragraph{Заполнитель} - это также специальная последовательность, но она говорит компилятору, что на место этой последовательности необходимо вставить некий аргумент, который будет передан после строки, через запятую, при вызове данной функции \code{printf();}. Заполнитель начинается со знака процента и обозначает тип вставляемой переменной. \begin{itemize} \item \code{\%\%} - символ процента; \item \code{\%i (\%d)} - целые числа (integer, decimal); \item \code{\%ld (\%li)} – целые числа (long int); \item \code{\%s} - для вывода строк; \item \code{\%c} – для вывода символов; \item \code{\%p} - для вывода указателей; \item \code{\%f} – для вывода чисел с плавающей точкой; \item \code{\%lf} – для вывода чисел с плавающей точкой удвоенной точности; \item \code{\%x (\%X)} - беззнаковое целое в шестнадцатеричном виде. \end{itemize} Как видно, существуют заполнители для всех основных типов данных и для экранирования самого символа начала заполнителя. Заполнители можно и нужно комбинировать в строках как между собой, так и с экранированными последовательностями. Умение работать с заполнителями пригодится не только в консоли, но и при формировании локализованных сообщений в более сложных приложениях, в том числе на других языках. \begin{figure}[h!] \begin{lstlisting}[language=C,style=CCodeStyle] int a = 50; printf("%d\n", a); printf("%5d\n", a); printf("%05d\n", a); printf("%.2f\n", 5.12345); printf("Placeholders are \"%5d%%\" of formatting\n", a); \end{lstlisting} \end{figure} Так первый оператор выведет просто число. Второй – это же число, но оставив для его отображения пять пробельных символов (два окажутся заняты разрядами числа 50, и ещё три останутся свободными слева от числа). Третий оператор форматированного вывода впишет число в пять отображаемых символов, но заполнит пустоту нулями (запись с лидирующими нулями, leading zeroes). Четвёртый осуществит вывод числа с плавающей точкой, ограничив дробную часть двумя отображаемыми символами, при этом важно, что не произойдёт математического округления, символы просто не отобразятся, такое отображение часто используется для демонстрации денежных значений в долларах и центах, рублях и копейках, и пр. Последний же оператор выведет на экран надпись, информирующую о том, что заполнители - это 50\% форматирования: \begin{figure}[h!] \begin{verbatim} $ ./program 50 50 00050 5.12 Placeholders are " 50%" of formatting $ \end{verbatim} \end{figure} Для заполнителей \code{\%d}, \code{\%i}, \code{\%f} часто используются дополнительные параметры, такие как количество знаков после запятой, например, \code{\%.2f} или минимальное количество знаков для отображения целого числа \code{\%5d}. Также в пользу оператора форматированного вывода говорит тот факт, что, например, в С++ стандартный вывод в консоль осуществляется с помощью команды \code{std::cout}, которая не поддерживала форматирование строк вплоть до принятия стандарта С++20. \subsection{Форматированный ввод} Поговорив о выводе в консоль, нельзя не сказать о пользовательском вводе данных. Один из способов пользовательского ввода данных в программу - это использование функции \code{scanf();}. Предложим пользователю ввести некоторое число: \begin{figure}[h!] \begin{lstlisting}[language=C,style=CCodeStyle] int input; printf("Please, enter a number: "); scanf("%d", &input); \end{lstlisting} \end{figure} Функция \code{scanf();} – это функция форматированного ввода. Принцип её работы очень похож на принцип работы функции \code{printf();} В двойных кавычках мы указываем в виде заполнителя тип переменной, которую ожидаем от пользователя, а в качестве дополнительного аргумента указываем адрес той переменной, в которую хотим записать введённые пользователем данные. Получается процесс прямо противоположный выводу. В этой функции можно использовать все те же заполнители, что и при выводе, поэтому пользователь может ввести как целые числа, так и символы, строки и числа с плавающей точкой. \begin{lstlisting}[language=C,style=CCodeStyle] printf("You entered %d, let's double it: %d\n", input, input * 2); \end{lstlisting} Выведем в консоль изменённое число, введённое пользователем, чтобы удостовериться, что всё работает. В результате запуска программы, консоль застынет в ожидании пользовательского ввода. Пользователь сообщает консоли (терминалу операционной системы) об окончании ввода нажатием клавиши \code{Enter}: \begin{figure}[h!] \begin{verbatim} $ ./program Please, enter a number: 50 You entered 50, let's double it: 100 $ \end{verbatim} \end{figure} Функция форматированного ввода позволяет не только приводить пользовательский ввод к нужному типу данных, но и считывать разные сложные пользовательские вводы из множества переменных, разделённых символами.