sonyps4.ru

Си как работает printf и scanf. Изучение и использование функций printf() и scanf()

Функции printf() и scanf() дают нам возможность взаимодействовать с программой. Мы называем их функциями ввода-вывода. Это не единственные функции, которыми мы можем воспользоваться для ввода и вывода данных с помощью программ на языке Си, но они наиболее универсальны. Эти функции входят в описание языка Си и они даны в библиотеке stdio.h. Обычно функции printf() и scanf() работают во многом одинаково - каждая использует управляющую строку и список аргументов. Сначала мы рассмотрим работу функции printf(), затем scanf().

Формат

Тип выводимой информации

десятичное целое число

один символ

строка символов

экспоненциальная запись

число с плавающей точкой, десятичная запись

используется вместо записи %f или %e

десятичное целое число без знака

восьмеричное целое число без знака

шестнадцатеричное целое число без знака

Инструкции, передаваемые функции printf(), когда мы хотим напечатать некоторую переменную, зависят от того, какого типа эта переменная. Например, при выводе на печать целого числа применяется формат %d, а при выводе символа - %c. Форматы перечислены в таблице.

Посмотрим теперь, как эти формы применяются:

/* печать */

#include

#define PI 3.14159

int number = 2003;

printf("Интернет-университет информационных технологий был открыт в %d году \n", number);

printf("Значение числа pi равно %f.\n", PI);

Формат, указываемый при обращении к функции printf(), выглядит следующим образом:

printf(Управляющая строка, аргумент1, аргумент2,_);

аргумент 1, аргумент 2 и т.д. - это печатаемые параметры, которые могут быть переменными, константами или даже выражениями , вычисляемыми перед выводом на печать.

Управляющая строка - строка символов, показывающая, как должны быть напечатаны параметры. Например, в операторе

printf("%d студентов получили оценку %f.\n",

управляющей строкой служит фраза в кавычках, а number и z - аргументы или в данном случае значения двух переменных.

Мы видим, что в управляющей строке содержится информация двух различных типов:

    Символы, печатаемые текстуально.

    Идентификаторы данных, называемые также спецификациями преобразования .

Каждому аргументу из списка, следующего за управляющей строкой, должна соответствовать одна спецификация преобразования . Если нужно напечатать какую-нибудь фразу, то нет необходимости использовать спецификацию преобразования ; если же требуется только вывести данные на печать, то можно обойтись и без использования комментария. Поэтому каждый из операторов , приведенных ниже, вполне приемлем:

printf("Эта книга не очень дорогая!\n");

printf("%c%d\n","$",cost);

Если нужно напечатать сам символ %, то компилятор примет его за ошибочную спецификацию преобразования . Выходом из создавшейся ситуации служит довольно простое решение - писать два символа % подряд.

Например:

printf("Только %d%% населения способно учиться самостоятельно! \n",i);

Результат работы программы будет выглядеть следующим образом:

Только 5% населения способно учиться самостоятельно!

Мы можем расширить основное определение спецификации преобразования , поместив модификаторы между знаком % и символом, определяющим тип преобразования. При использовании одновременно нескольких модификаторов они должны быть указаны в том порядке, в котором перечислены в таблице.

Модификаторы

Значение

Аргумент будет печататься с левой позиции поля заданной ширины. Обычно печать аргумента оканчивается в самой правой позиции поля. Пример: %-10

строка цифр

Задает минимальную ширину поля. Большее поле будет использоваться, если печатаемое число или строка не помещается в исходном поле Пример: %4d

строка цифр

Определяет точность: для типов данных с плавающей точкой число печатаемых цифр справа от десятичной точки; для символьных строк - максимальное число печатаемых символов Пример:

%4.2f(две десятичные цифры для поля шириной в четыре символа)

Соответствующий элемент данных имеет тип long, а не int Пример: %ld

printf("/%d/\n",135);

printf("/%2d/\n",135);

printf("/%10d/\n",135);

printf("/%-10d/\n",135);

Первая спецификация преобразования %d не содержит модификаторов. Это так называемый выбор по умолчанию, т. е. результат действия компилятора в случае, если вы не дали ему никаких дополнительных инструкций. Вторая спецификация преобразования - %2d. Она указывает, что ширина поля должна равняться 2, но, поскольку число состоит из трех цифр, поле автоматически расширяется до необходимого размера. Следующая спецификация %10d показывает, что ширина поля равна 10. Последняя спецификация %-10d также указывает ширину поля, равную 10, а знак - приводит к сдвигу всего числа к левому краю.

Теги: Форматированный ввод, форматированный вывод, printf, scanf, fgets, getch, строка формата, спецификатор формата, флаги, управляющие символы.

Форматированный вывод

Сегодня мы рассмотрим две важные функции форматированного ввода и вывода. Устройство и работу этих функций полностью можно понять только после изучения работы с указателями и функций с переменным числом параметров. Но пользоваться этими функциями необходимо уже сейчас, так что некоторые моменты придётся пропустить.

Функция форматированного вывода printf получает в качестве аргументов строку формат и аргументы, которые необходимо вывести в соответствии с форматом, и возвращает число выведенных символов. В случае ошибки возвращает отрицательное значение и устанавливает значение ferror. Если произошло несколько ошибок, errno равно EILSEQ.
int printf (const char * format, ...);

#include #include void main() { //функция не получает никаких аргументов, кроме строки printf("Hello world"); getch(); }

Функция проходит по строке и заменяет первое вхождение % на первый аргумент, второе вхождение % на второй аргумент и т.д. Далее мы будем просто рассматривать список флагов и примеры использования.

Общий синтаксис спецификатора формата
%[флаги][ширина][.точность][длина]спецификатор
Спецификатор – это самый важный компонент. Он определяет тип переменной и способ её вывода.

Таб. 1 Спецификатор типа.
Спецификатор Что хотим вывести Пример
d или i Целое со знаком в в десятичном виде 392
u Целое без знака в десятичном виде 7235
o Беззнаковое в восьмеричном виде 657
x Беззнаковое целое в шестнадцатеричном виде 7fa
X Беззнаковое целое в шестнадцатеричном виде, верхний регистр 7FA
f или F Число с плавающей точкой 3.4563745
e Экспоненциальная форма для числа с плавающей точкой 3.1234e+3
E Экспоненциальная форма для числа с плавающей точкой, верхний регистр 3.1234E+3
g Кратчайшее из представлений форматов f и e 3.12
G Кратчайшее из представлений форматов F и E 3.12
a Шестнадцатеричное представление числа с плавающей точкой -0xc.90fep-2
A Шестнадцатеричное представление числа с плавающей точкой, верхний регистр -0xc.90FEP-2
c Буква a
s Строка (нуль-терминированный массив букв) Hello World
p Адрес указателя b8000000
n Ничего не пачатает. Аргументом должен быть указатель на signed int. По этому адресу будет сохранено количество букв, которое было выведено до встречи %n
% Два идущих друг за другом процента выводят знак процента %

#include #include void main() { int a = 0x77, b = -20; char c = "F"; float f = 12.2341524; double d = 2e8; char* string = "Hello, World!"; printf("%s\n", string); printf("a = %d, b = %d\n", a, b); printf("a = %u, b = %u\n", a, b); printf("a = %x, b = %X\n", a, b); printf("dec a = %d, oct a = %o, hex a = %x\n", a, a, a); printf("floating point f = %f, exp f = %e\n", f, f); printf("double d = %f or %E\n", d, d); printf("not all compiler support %a\n", f); printf("character c = %c, as number c = %d", c, c); printf("address of string is %p", string); getch(); }

Строка формата также может включать в себя следующие необязательные суб-спецификаторы: флаг, ширина, .точность и модификатор (именно в таком порядке).

Таб. 4 Точность.
.Точность Описание
.число Для спецификаторов целых (d, i, o, u, x, X) точность определяет минимальное количество знаков, которое необходимо вывести. Если значение короче, то выводятся нули перед числом. Значение не обрезается, даже если оно длиннее. Точночть 0 означает, что для значения 0 ничего не выводится.
Для спецификаторов чисел с плавающей точкой (a, A, e, E, f, F) это число знаков, которые необходимо вывести после десятичной точки (по умолчанию 6).
Для g и G - это число значащих разрядов, которые необходимо вывести.
Для s - выводится указанное число символов. По умолчанию выводятся все символы до первого нулевого.
Если число не стоит, то по умолчанию точность равна 0
.* Точность не указана в строке формата, она передаётся отдельно в виде аргумента, который должен предшествовать выводимому числу

#include #include void main() { int a = 0x77, b = -20; char c = "F"; float f = 12.2341524; double d = 2e2; char* string = "Hello, World!"; printf("%.3f\n", f); printf("%.*f\n", 2, f); printf("%010.3f\n", d); printf("%*d\n", 6, a); printf("%+d\n", b); printf("%0.6d\n", a); printf("%.f\n", d); printf("%.4s", string); getch(); }

Суб-спецификатор длины изменяет длину типа. В случае, если длина не совпадает с типом, по возможности происходит преобразование до нужного типа.

Таб. 5 Длина.
спецификаторы
Длина d, i u o x X f F e E g G a A c s p n
(none) int unsigned int double int char* void* int*
hh signed char unsigned char signed char*
h short int unsigned short int short int*
l long int unsigned long int wint_t wchar_t* long int*
ll long long int unsigned long long int long long int*
j intmax_t uintmax_t intmax_t*
z size_t size_t size_t*
t ptrdiff_t ptrdiff_t ptrdiff_t*
L long double

#include #include void main() { long long x = 12300000000579099123; short i = 10; printf("%llu\n", x); printf("%d\n", i); printf("%hd\n", i); getch(); }

Форматированный ввод

Рассмотрим форматированный ввод функцией scanf.
int scanf(const char*, ...)
Функция принимает строку формата ввода (она похожа на строку формата printf) и адреса, по которым необходимо записать считанные данные. Возвращает количество успешно проинициализированных аргументов.
Формат спецификатора ввода
%[*][ширина][длинна]спецификатор

Таб. 6 Спецификатор типа.
Спецификатор Описание Выбранные символы
i, u Целые Произвольное число цифр (0-9), возможно, начинающихся с + или -. Если число начинается с 0, то считывается в восьмеричном формате, если с 0x, то в шестнадцатеричном.
d Десятичное целое Произвольное число цифр (0-9), возможно, начинающихся с + или -.
o восьмеричное целое Произвольное число цифр (0-7), возможно, начинающихся с + или -.
x Шестнадцатеричное целое Произвольное число цифр (0-F), возможно, начинающихся с + или - и префикса 0x или 0X.
f, e, g Число с плавающей точкой Число, состоящее из набора цифр 0-9, возможно с десятичным разделителем (точкой). Возможно также представление в экспоненциальной форме. C99 позволяет также вводить число в шестнадцатеричном формате.
a
c Символ Если ширина не передана, то считывает один символ. Если ширина передана, то считывает нужное количество символов и размещает их в массиве БЕЗ терминального символа на конце.
s Строка Считывает все не пробельные символы. Если указана ширина, то не более n символов. Ставит на место n+1 символа терминальный.
p Адрес указателя Последовательность символов, трактуемая как адрес указателя. Формат зависит от реализации, но совпадает с тем, как выводит printf с ключом p
[символы] Множество символов Считывает только те символы, которые записаны в квадратных скобках, С99
[^символы] Множество символов Считывает только те символы, которые не указаны в квадратных скобках, С99
n Ничего не считывает Сохраняет число уже считанных символов по указанному адресу

Как и в printf, ширина, заданная символом * ожидает аргумента, который будт задавать ширину. Флаг длина совпадает с таким флагом функции printf.

#include #include void main() { int year, month, day; char buffer; int count; //Требует форматированного ввода, например 2013:12:12 printf("Enter data like x:x:x = "); scanf("%d:%d:%d", &year, &month, &day); printf("year = %d\nmonth = %d, day = %d\n", year, month, day); //Считываем строку, не более 127 символов. При считывании в массив писать & не надо, //так как массив подменяется указателем printf("Enter string = "); scanf("%127s", buffer); printf("%s", buffer); getch(); }

Кроме функций scanf и printf есть ещё ряд функций, которые позволяют получать вводимые данные

int getch() - возвращает введённый символ, при этом не выводит его на консоль. #include #include void main() { char c = 0; do { c = getch(); printf("%c", c); } while (c != "q"); }

char * fgets (char * str, int num, FILE * stream) - функция позволяет считывать строку с пробельными символами. Несмотря на то, что она работает с файлом, можно с её помощью считывать и из стандартного потока ввода. Её преимущество относительно gets в том, что она позволяет указать максимальный размер считываемой строки и заканчивает строку терминальным символом.

#include #include #include void main() { char buffer; //Считываем из стандартного потока ввода fgets(buffer, 127, stdin); printf("%s", buffer); //Этим можно заменить ожидание ввода символа scanf("1"); }

Это не полный набор различных функций символьного ввода и вывода. Таких функций море, но очень многие из них небезопасны, поэтому перед использованием внимательно читайте документацию.

В данной статье функция scanf() рассматривается в общем виде без привязки к конкретному стандарту, поэтому сюда включены данные из любых стандартов C99, C11, C++11, C++14. Возможно, в некоторых стандартах функция работает с отличиями от изложенного в статье материала.

Функция scanf C - описание

scanf() - это функция, расположенная в заголовочном файле stdio.h(C) и cstdio(C++), она также называется форматированным вводом данных в программу. scanf читает символы из стандартного потока ввода (stdin) и преобразует их в соответствии с форматом, после чего записывает в указанные переменные. Формат - означает, что данные при поступлении приводятся к определенному виду. Таким образом, функция scanf C описывается:

scanf("%формат", &переменная1[, &переменная2,[…]]),

где переменные передаются в виде адресов. Причина такого способа передачи переменных в функцию очевидна: в результате работы она возвращает значение, указывающее на наличие ошибок, поэтому единственным способом изменять значения переменных является передача по адресу. Также, благодаря такому способу, функция может обрабатывать данные любых типов.

Некоторые программисты из-за аналогии с другими языками называют функции, подобные scanf() или printf(), процедурами.

Scanf позволяет осуществлять ввод всех базовых типов языка: char, int, float, string и т.д. В случае с переменными типа string нет нужды указывать знак адреса - «&», так как переменная типа string является массивом, и имя ее является адресом первого элемента массива в памяти компьютера.

Формат ввода данных или управляющая строка

Начнем с рассмотрения примера использования функции scanf C из описания.

#include int main() { int x; while (scanf("%d", &x) == 1) printf("%d\n", x); return 0; //требование linux-систем }

Формат ввода состоит из следующих четырех параметров: %[*][ширина][модификаторы] тип. При этом знак «%» и тип являются обязательными параметрами. То есть, минимальный вид формата выглядит следующим образом: “%s”, “%d” и так далее.

В общем случае символы, составляющие строку формата, делятся на:

  • спецификаторы формата - все, что начитается с символа %;
  • разделительные или пробельные символы - ими считаются пробел, табуляция(\t), новая строка (\n);
  • символы, отличающиеся от пробельных.

Функция может оказаться небезопасной.

Используйте вместо scanf() функцию scanf_s().

(сообщение от Visual Studio)

Тип, или спецификаторы формата, или литеры преобразования, или контролирующие символы

Описание scanf C обязано содержать, как минимум, спецификатор формата, который указывается в конце выражений, начинающихся со знака «%». Он сообщает программе тип данных, который следует ожидать при вводе, обычно с клавиатуры. Список всех спецификаторов формата в таблице ниже.

Значение

Программа ожидает ввод символа. Переменная для записи должна иметь символьный тип char.

Программа ожидает ввод десятичного числа целого типа. Переменная должна иметь тип int.

Программа ожидает ввод числа с плавающей точкой (запятой) в экспоненциальной форме. Переменная должна иметь тип float.

Программа ожидает ввод числа с плавающей точкой (запятой). Переменная должна иметь тип float.

7

Программа ожидает ввод числа с плавающей точкой (запятой). Переменная должна иметь тип float.

Программа ожидает ввод восьмеричного числа. Переменная должна иметь тип int.

Программа ожидает ввод строки. Строкой считается набор любых символов до первого встреченного разделительного символа. Переменная должна иметь тип string.

Программа ожидает ввод шестнадцатеричного числа. Переменная должна иметь тип int.

Переменная ожидает ввод указателя. Переменная должна иметь тип указателя.

Записывает в переменную целое значение, равное количеству считанных до текущего момента символов функцией scanf.

Программа считывает беззнаковое целое число. Тип переменной должен быть unsigned integer.

Программа ожидает ввод двоичного числа. Переменная должна иметь тип int.

Набор сканируемых символов. Программа ожидает ввод символов, из ограниченного пула, указанного между scanf будет работать до тех пор, пока на потоке ввода находятся символы из указанного множества.

Символы в строке формата

Символ звездочка (*)

Звездочка (*) - это флаг, указывающий, что операцию присвоения надо подавить. Звездочка ставится сразу после знака «%». Например,

Scanf("%d%*c%d", &x, &y); //игнорировать символ между двумя целыми числами. scanf("%s%*d%s", str, str2); //игнорировать целое число, между двумя строками.

То есть, если ввести в консоли строку «45-20» программа сделает следующее:

  1. Переменной «x» будет присвоено значение 45.
  2. Переменной «y» будет присвоено значение 20.
  3. А знак минус(тире) «-» будет проигнорирован благодаря «%*c».

Ширина (или ширина поля)

Это целое число между знаком «%» и спецификатором формата, которое определяет максимальное количество символов для считывания за текущую операцию чтения.

Следует иметь в виду несколько важных моментов:

  1. scanf прекратит свою работу, если встретит разделительный символ, даже если не считал 20 символов.
  2. Если на ввод подается больше 20 символов, в переменную str будут записаны только первые 20 из них.

Модификаторы типа (или точность)

Это специальные флаги, которые модифицируют тип данных, ожидаемых к вводу. Флаг указывается слева от спецификатора типа:

  • L или l (маленькая L) При использовании «l» со спецификаторами d, i, o, u, x, флаг сообщает программе, что ожидается ввод данных типа long int. При использовании «l» со спецификатором e или f, флаг сообщает программе, что она должна ожидать ввод значения типа double. Использование «L» сообщает программе, что ожидается значение типа long double. Использование «l» со спецификаторами «c» и «s» сообщает программе, что ожидаются двухбайтовые символы типа wchar_t. Например, "%lc", "%ls", "%l".
  • h - флаг, указывающий на тип short.
  • hh - обозначает, что переменная является указателем на значение типа signed char или unsigned char. Флаг можно использовать со спецификаторами d, i, o, u, x, n.
  • ll (две маленькие L) - обозначает, что переменная является указателем на значение типа signed int или unsigned long long int. Флаг используется со спецификаторами: d, i, o, u, x, n.
  • j - обозначает, что переменная является указателем на тип intmax_t или uintmax_t из заголовочного файла stdint.h. Используется со спецификаторами: d, i, o, u, x, n.
  • z - обозначает, что переменная является указателем на тип size_t, определение которого находится в stddef.h. Используется со спецификаторами: d, i, o, u, x, n.
  • t - обозначает, что переменная является указателем на тип ptrdiff_t. Определение на этот тип находится в stddef.h. Используется со спецификаторами: d, i, o, u, x, n.

Более явно картину с модификаторами можно представить в виде таблицы. Такое описание scanf C для програмистов будет понятнее.

Остальные символы

Любые символы, которые будут встречены в формате, будут отбрасываться. При этом стоит отметить, что наличие в управляющей строке пробельных или разделительных символов (новая строка, пробел, табуляция) может приводить к разному поведению функции. В одной версии scanf() будет читать без сохранения любое количество разделителей до момента, пока не встретит символ, отличный от разделителя, а в другой версии - пробелы (только они) не играют роли и выражение "%d + %d" эквивалентно "%d+%d".

Примеры

Рассмотрим ряд примеров, позволяющих поразмыслить и точнее понять работу функции.

Scanf("%3s", str); //если ввести в консоли строку «1d2s3d1;3», в str запишется только «1d2» scanf("%dminus%d", &x, &y); //символы «minus» между двумя числами будут отброшены scanf("%5", str); //ввод символов в str будет происходить до тех пор, пока их не будет 5 и символы являются числами от 0 до 9. scanf("%lf", &d); //ожидается ввод данных типа double scanf("%hd", &x); //ожидается число типа short scanf("%hu", &y); //ожидается число типа unsigned short scanf("lx", &z); //ожидается число типа long int

Из приведенных примеров видно, как меняется ожидаемое число с использованием различных символов.

scanf C - описание для начинающих

Данный раздел будет полезен новичкам. Зачастую нужно иметь под рукой не столько полное описание scanf C, сколько детали работы функции.

  • Функция является отчасти устаревшей. Существует несколько разных реализаций в библиотеках различных версий. Например, усовершенствованная функция scanf S C, описание которой можно найти на сайте microsoft.
  • Количество спецификаторов в формате должно соответствовать количеству переданных функции аргументов.
  • Элементы входного потока должны отделяться только разделительными символами: пробел, табуляция, новая строка. Запятая, точка с запятой, точка и т. д. - эти символы не являются разделительными для функции scanf().
  • Если scanf встретит разделительный символ, ввод будет остановлен. Если переменных для чтения больше одной, то scanf перейдет к чтению следующей переменной.
  • Малейшее несоответствие формата вводимых данных приводит к непредсказуемым результатам работы программы. Хорошо, если программа просто завершится с ошибкой. Но нередко программа продолжает работать и делает это неверно.
  • scanf("%20s …", …); Если входной поток превышает 20 символов, то scanf прочитает первые 20 символов и, либо прекратит работу, либо перейдет к чтению следующей переменной, если она указана. При этом следующий вызов scanf продолжит чтение входного потока с того места, где остановилась работа предыдущего вызова scanf. Если при чтении первых 20 символов будет встречен разделительный символ, scanf прекратит свою работу или перейдет к чтению следующей переменной, даже если не считал 20 символов для первой переменной. При этом все несчитанные символы прицепятся к следующей переменной.
  • Если набор сканируемых символов начать со знака «^», то scanf будет читать данные до тех пор, пока не встретит разделительный символ или символ из набора. Например, "%[^A-E1-5]" будет считывать данные из потока, пока не будет встречен один из символов английского алфавита от А до Е в верхнем регистре или одно из чисел от 1 до 5.
  • Функция scanf C по описанию возвращает число, равное успешному количеству записей в переменные. Если scanf записывает 3 переменные, то результатом успешной работы функции будет возврат числа 3. Если scanf не смог записать ни одной переменной, то результат будет 0. И, наконец, если scanf вообще не смог начать работать по каким-либо причинам, результатом будет EOF.
  • Если функция scanf() завершила свою работу некорректно. Например, scanf("%d", &x) - ожидалось число, а на ввод пришли символы. Следующий вызов scanf() начнет свою работу с того места в потоке ввода, где завершился предыдущий вызов функции. Чтобы преодолеть эту проблему, необходимо избавиться от проблемных символов. Это можно сделать, например, вызвав scanf("%*s"). То есть, функция прочитает строку символов и выбросит ее. Таким хитрым образом можно продолжить ввод нужных данных.
  • В некоторых реализациях scanf() в наборе сканируемых символов недопустимо использование «-».
  • Спецификатор “%c” читает каждый символ из потока. То есть символ -разделитель он также читает. Чтобы пропустить символ разделитель и продолжить читать нужный символ, можно использовать “%1s”.
  • При использовании спецификатора «c» допустимо использовать ширину “%10c”, однако тогда в виде переменной функции scanf нужно передать массив элементов типа char.
  • “%” - это значит "все маленькие буквы английского алфавита", а “%” - значит просто 3 символа: ‘z’, ‘a’, ‘-’. Иными словами, символ «-» означает диапазон только в том случае, если стоит между двумя символами, которые находятся в правильном порядке следования. Если «-» находится в конце выражения, в начале или в неверном порядке символов по обеим сторонам от них, то он представляет собой просто символ дефиса, а не диапазон.

Заключение

На этом завершается описание scanf C. Это хорошая удобная функция для работы в небольших программах и при использовании процедурного метода программирования. Однако главным недостатком является количество непредсказуемых ошибок, которые могут возникнуть при использовании scanf. Поэтому, описание scanf C при програмировании лучше всего держать перед глазами. В крупных профессиональных проектах используются потоки iostream, ввиду того, что обладают более высокоуровневыми возможностями, лучше позволяют отлавливать и обрабатывать ошибки, а также работать со значительными объемами информации. Также следует отметить, описание scanf C на русском доступно на сетевых многих источниках, как и примеры ее использования, ввиду возраста функции. Поэтому при необходимости всегда можно найти ответ на тематических форумах.

В то время как функция printf() осуществляет форматированный вывод данных, функция scanf() осуществляет их форматированный ввод. Это значит, что поступающие на ввод данные преобразуются соответственно указанному формату(ам) и записываются по адресу(ам) указанной(ых) переменной(ых):

Scanf(строка_формата, адреса_переменных);

Причина, по которой в scanf() передаются адреса, а не значения переменных, очевидна. Функция scanf() должна изменять значения переменных тех функций, из которых вызывается. Единственный способ - это получить адреса областей памяти.

Спецификации формата данных, допустимые в строке формата, для scanf() почти идентичны тем, что были описаны для функции printf() . На этом уроке мы подробно не рассмотрим все возможности форматированного ввода с помощью scanf() , зато разберем ряд конкретных примеров.

Ввод чисел, символов и строк

Пример ввода-вывода целого и вещественного чисел, символа и строки:

int a; float b; char ch, str[ 30 ] ; scanf ("%d%f%c%s" , & a, & b, & ch, str) ; printf ("%d %.3f %c %s\n " , a, b, ch, str) ;

Результат:

45 34.3456y hello 45 34.346 y hello

Здесь при выполнении программы все данные были введены в одну строку. Разделителем между числами и строками является пробел, а также любой другой символ пустого пространства (например, "\n"). Однако при считывании символа, пробел учитывается как символ; чтобы этого не произошло, в примере букву записали сразу после числа. Данные можно было бы ввести, разделяя их переходом на новую строку (опять же при этом надо иметь ввиду, как считывается символ).

В строке формата функции scanf() между спецификациями вполне допустимо поставить пробелы: %d %f %c %s . Они никакой роли не сыграют. Понятно, что данные можно было получить и так:

scanf ("%d" , & a) ; scanf ("%f" , & b) ; scanf ("%c" , & ch) ; scanf ("%s" , str) ;

Обратите внимание, перед переменной str отсутствует знак амперсанда. Это не опечатка. В последующих уроках вы узнаете, что имя массива уже само по себе является ссылкой на массив (другими словами, str содержит адрес начала массива).

В функции scanf() в спецификации формата вещественных чисел не указывается точность представления числа. Запись типа %.3f или %.10lf приведет к невозможности получить вещественное число. Чтобы получить число типа double используют формат %lf, для long double - %Lf.

Для целых чисел: длинное целое - %ld, короткое целое - %hd. Кроме того, существуют спецификации для ввода восьмеричных и шестнадцатеричных чисел.

Функция scanf() возвращает количество удачно считанных данных; т.е. значение, возвращаемое функцией, можно проанализировать и таким образом узнать, корректно ли были введены данные. Например:

int a; double b; char ch, str[ 30 ] ; ch = scanf ("%d %lf %s" , & a, & b, str) ; if (ch == 3 ) printf ("%d %.3lf %s\n " , a, b, str) ; else printf ("Error input\n " ) ;

Использование обычных символов

В строке формата scanf() допустимо использование обычных символов. В этом случае при вводе данных также должны вводится и эти символы:

int a, b, c; scanf ("%d + %d = %d" , & a, & b, & c) ; printf ("Your answer is %d\n The correct answer is %d\n " , c, a+ b) ;

В данном случае, когда программа выполняется, ввод должен выглядеть примерно так: 342+1024 = 1366. Знаки "+" и "=" обязательно должны присутствовать между числами, наличие пробелов или их отсутствие абсолютно никакой роли не играет:

45 + 839=875 Your answer is 875 The correct answer is 884

Запрет присваивания

Если какие-либо данные, вводимые пользователем, следует проигнорировать, то используют запрет присваивания, ставя после знака %, но перед буквой формата звездочку *. В таком случае данные считываются, но никакой переменной не присваиваются. Это можно использовать, например, когда нет определенной уверенности в том, что поступит на ввод, с одной стороны, и нужды сохранять эти данные, с другой:

float arr[ 3 ] ; int i; for (i = 0 ; i < 3 ; i++ ) scanf ("%*s %f" , & arr[ i] ) ; printf ("Sum: %.2f\n " , arr[ 0 ] + arr[ 1 ] + arr[ 2 ] ) ;

Здесь предполагается, что перед каждым числом будет вводится строка, которую следует проигнорировать, например:

First: 23.356 Second: 17.285 Third: 32.457 Sum: 73.098

Использование "шаблонов"

Для функции scanf() есть пара спецификаций формата, отдаленно напоминающих шаблоны командной оболочки и др. Формат […] позволяет получить строку, содержащую любые символы, указанные в квадратных скобках. Как только на ввод поступает символ, не входящий в указанный набор, считывание данных прекращается. Формат [^…], наоборот, помещает в строку символы, не входящие в указанный набор, до тех пор пока не встретит любой из указанных.

В примере ниже как только поступает не цифра, считывание ввода завершается. При этом если первый символ - не цифра, то в str вообще ничего не записывается:

char str[ 30 ] = "" ; scanf ("%" , str) ; printf ("%s\n " , str) ;

А в этом случае строке будет присвоена последовательность символов до любого из указанных знаков препинания:

scanf ("%[^;:,!?]" , str) ; printf ("%s\n " , str) ;

Результат:

Hello, World! Hello

Некоторые особенности и ограничения функции scanf()

Как только поступают некорректные данные, функция scanf() завершает свою работу. В примере:
scanf ("%d%f" , & a, & b) ;

если переменной a попытаться присвоить символ или строку, что невозможно, то переменная bуже обрабатываться не будет. Можно предположить, что так будет надежнее:

scanf ("%d" , & a) ; scanf ("%f" , & b) ;

Вроде бы неудачное считывание a не должно оказывать никакого влияния на b, т.к. это уже иной вызов scanf() . Но не все так просто: при некорректном вводе данные остаются в буфере и пытаются "навязать" себя последующим вызовам scanf() . Поэтому при использовании scanf() надо думать о том, как в случае некорректного ввода очистить буфер. Например, это можно сделать так, как показано ниже, или путем использования специальных функций (здесь не рассматриваются):

if (scanf ("%d" , & a) != 1 ) // если данные не удалось присвоить переменной, scanf ("%*s" ) ; // то выбросить их в виде строки. scanf ("%f" , & b) ;

Разделителем данных для scanf() являются символы пустого пространства. Это означает отсутствие возможности записать строку, содержащую неизвестное количество пробелов, в одну переменную, используя только scanf() . Придется использовать либо другую функцию (например, getchar()), либо создать циклическую конструкцию, считывающую по одному слову и добавляющую его к общей строке.

Таким образом, не смотря на достаточно большие возможности scanf() , эта функция хранит в себе ряд неудобств и опасностей.

Решение задач

  1. На прошлом занятии вы написали программу, содержащую функции, вычисляющие факториал числа и заданный элемент ряда Фибоначчи. Измените эту программу таким образом, чтобы она запрашивала у пользователя, что он хочет вычислить: факториал или число Фибоначчи. Затем программа запрашивала бы у пользователя либо число для вычисления факториала, либо номер элемента ряда Фибоначчи.
  2. Напишите программу, которая запрашивает у пользователя две даты в формате дд.мм.гггг. Дни, месяцы и года следует присвоить целочисленным переменным. Программа должна выводить на экран информацию о том, какая дата более ранняя, а какая более поздняя.
  3. Используя цикл, напишите код, в котором пользователю предлагается вводить данные до тех пор, пока он не сделает это корректно, т.е. пока все указанные в scanf() переменные не получат свои значения. Протестируйте программу.
#include
int scanf (const char *format, ...);

Функция scanf() представляет собой процедуру ввода общего назначения, которая читает поток stdin и сохраняет информацию в переменных, перечисленных в списке аргументов. Она может читать все встроенные типы данных и автоматически преобразовывать их в соответствующий внутренний формат.

В версии С99 к параметру format применен квалификатор restrict .

Управляющая строка, задаваемая параметром format , состоит из символов трех категорий:

  • спецификаторов формата;
  • пробельных символов;
  • символов, отличных от пробельных.

  • Спецификации формата начинаются знаком % и сообщают функции scanf() тип данного, которое будет прочитано. Например, по спецификации %s будет прочитана строка, а по спецификации %d - целое значение. Строка форматирования читается слева направо, и спецификации формата сопоставляются аргументам в порядке их перечисления в списке аргументов.

    Спецификации формата функции scanf() :
    Читает значение с плавающей точкой (только С99)
    Аналогично коду (только С99)
    Читает один символ
    %d Читает десятичное целое
    %i Читает целое в любом формате (десятичное, восьмеричное или шести ад цате ричное)

    Аналогично коду
    %f Читает число с плавающей точкой
    %F Аналогично коду %f (только С99)
    %g Читает число с плавающей точкой
    %G Аналогично коду %g
    %o Читает восьмеричное число
    %s Читает строку
    Читает шести ад цате ричное число
    Аналогично коду
    Читает указатель
    %n Принимает целое значение, равное количеству прочитанных до сих пор символов
    %u Читает десятичное целое без знака
    % Просматривает набор символов
    %% Читает знак процента

    По умолчанию спецификации a , f , е и g заставляют функцию scanf() присваивать данные переменным типа float . Если перед одной из этих спецификаций поставить модификатор l , функция scanf() присвоит прочитанные данные переменной типа double . Использование же модификатора L означает, что полученное значение присвоится переменной типа long double .

    Современные компиляторы, поддерживаюшие добавленные в 1995 году средства работы с двухбайтовыми символами, позволяют к спецификации с применить модификатор l ; тогда будет считаться, что соответствующий указатель указывает на двухбайтовый символ (т.е. на данное типа wchar_t ). Модификатор l также можно использовать с кодом формата s ; тогда будет считаться, что соответствующий указатель указывает на строку двухбайтовых символов. Кроме того, модификатор l можно использовать для того, чтобы указать, что набор сканируемых символов состоит из двухбайтовых символов.

    Если в строке форматирования встретится разделитель, то функция scanf() пропустит один или несколько разделителей во входном потоке. Под разделителем, или пробельным символом, подразумевается пробел, символ табуляции или разделитель строк (символ новой строки). По сути, наличие одного разделителя в управляющей строке приведет к тому, что функция scanf() будет читать, не сохраняя, любое количество (возможно, даже нулевое) разделителей до первого символа, отличного от разделителя.

    Если в строке форматирования встретился символ, отличный от разделителя, то функция scanf() прочитает и отбросит его. Например, если в строке форматирования встретится %d , %d , то функция scanf() сначала прочитает целое значение, затем прочитает и отбросит запятую и, наконец, прочитает еще одно целое. Если заданный символ не найден, функция scanf() завершает работу.
    Все переменные, получающие значения с помощью функции scanf() , должны передаваться посредством своих адресов. Это значит, что все аргументы должны быть указателями на переменные.
    Элементы входного потока должны быть разделены пробелами, символами табуляции или разделителями строк. Такие символы, как запятая, точка с запятой и т.п., не распознаются в качестве разделителей. Это означает, что оператор
    scanf ("%d%d", &r, &c); примет значения, введенные как 10 20, но откажется от последовательности символов 10,20.

    Символ * , стоящий после знака % и перед кодом формата, прочитает данные заданного типа, но запретит их присваивание. Следовательно, оператор
    scanf ("%d%*c%d", &x, &y); при вводе данных в виде 10/20 поместит значение 10 в переменную х, отбросит знак деления и присвоит значение 20 переменной у.

    Команды форматирования могут содержать модификатор максимальной длины поля. Он представляет собой целое число, располагаемое между знаком % и кодом формата, которое ограничивает количество читаемых для всех полей символов. Например, если в переменную address нужно прочитать не более 20 символов, используется следующий оператор.
    scanf ("%20s", address);
    Если входной поток содержит более 20 символов, то при последующем обращении к операции ввода чтение начнется с того места, в котором "остановился" предыдущий вызов функции scanf() . Если разделитель встретится раньше, чем достигнута максимальная длина поля, ввод данных завершится. В этом случае функция scanf() переходит к чтению следующего поля.

    Хотя пробелы, символы табуляции и разделители строк используются в качестве разделителей полей, при чтении одиночного символа они читаются подобно любому другому символу. Например, если входной поток состоит из символов х у, то оператор
    scanf ("%c%c%c", &a, &b, &c); поместит символ х в переменную а, пробел - в переменную b, а символ у - в переменную с.

    Помните, что любые символы управляющей строки (включая пробелы, символы табуляции и новой строки), не являющиеся спецификациями формата, используются для установки соответствия и отбрасывания символов из входного потока. Любой соответствующий им символ отбрасывается. Например, если поток ввода выглядит, как 10t20, оператор
    scanf ("%dt%d", &x, &y); присвоит переменной х значение 10, а переменной у - значение 20. Символ t отбрасывается, так как он присутствует в управляющей строке.

    Функция scanf() поддерживает спецификатор формата общего назначения, называемый набором сканируемых символов (scanset) , В этом случае определяется набор символов, которые могут быть прочитаны функцией scanf() и присвоены соответствующему массиву символов. Для определения такого набора символы, подлежащие сканированию, необходимо заключить в квадратные скобки. Открывающая квадратная скобка должна следовать сразу за знаком процента. Например, следующий набор сканируемых символов указывает на то, что необходимо читать только символы А, B и С.
    % При использовании набора сканируемых символов функция scanf() продолжает читать символы и помещать их в соответствующий массив символов до тех пор, пока не встретится символ, отсутствующий в заданном наборе. Соответствующая набору переменная должна быть указателем на массив символов. При возврате из функции scanf() этот массив будет содержать строку из прочитанных символов, завершающуюся символом конца строки.

    Если первый символ в наборе является знаком ^ , то получаем обратный эффект: входное поле читается до тех пор, пока не встретится символ из заданного набора сканируемых символов, т.е. знак ^ заставляет функцию scanf() читать только те символы, которые отсутствуют в наборе сканируемых символов.
    Во многих реализациях допускается задавать диапазон с помощью дефиса. Например, функция scanf() , встречая набор сканируемых символов в виде % , будет читать символы, попадающие в диапазон от А до Z.

    Важно помнить, что в наборе сканируемых символов различаются прописные и строчные буквы. Следовательно, чтобы сканировать как прописные, так и строчные буквы, в наборе сканируемых символов придется задать их отдельно.

    Функция scanf() возвращает число, равное количеству полей, для которых успешно присвоены значения. К этим полям не относятся поля, которые были прочитаны, но присвоение не состоялось в связи с использованием модификатора * , подавляющего присваивание. При обнаружении ошибки до присвоения значения первого поля функция scanf() возвращает значение EOF .

    Модификаторы формата, добавленные к функции scanf() Стандартом С99

    В версии С99 для использования в функции scanf() добавлены модификаторы формата hh , ll , j , z и t . Модификатор hh можно применять к спецификациям d , i , о , u , х и n signed char или unsigned char . Модификатор ll также можно применять к спецификациям d , i , о , u , х и n . Он означает, что соответствующий аргумент является указателем на значение типа signed long long int или unsigned long long int .

    Модификатор формата j d , i , о , u , х и n , означает, что соответствующий аргумент является указателем на значение типа intmax_t или uintmax_t . Эти типы объявлены в заголовке и служат для хранения целых максимально возможной разрядности.

    Модификатор формата z , который применяется к спецификациям d , i , о , u , х и n size_t и служит для хранения результата операции sizeof .

    Модификатор формата t , который применяется к спецификациям d , i , о , u , х и n , означает, что соответствующий аргумент является указателем на объект типа ptrdiff_t . Этот тип объявлен в заголовке и служит для хранения значения разности между двумя указателями.



    Загрузка...