Что возвращает scanf
Перейти к содержимому

Что возвращает scanf

int scanf(const char *format, arg-list)

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

Управляющая строка, на которую указывает format, состоит из символов трех типов:

  • Спецификаторы формата
  • Специальные символы
  • Прочие символы (не специальные)

Спецификаторы формата следуют за символом процент и сообщают scanf(), данные какого типа будут считаны следующими. Коды спецификаторов приведены в таблице.

Табпица: Коды форматов для scanf()

Код Значение
Считать один символ
%d Считать десятичное число целого типа
%i Считать десятичное число целого типа
Считать число с плавающей запятой
%f Считать число с плавающей запятой
%g Считать число с плавающей запятой
Считать восьмеричное число
%s Считать строку
Считать шестнадцатиричное число
Считать указатель
%n Принимает целое значение, равное количеству считанных до текущего момента символов
%u Считывает беззнаковое целое
%[] Просматривает набор символов
%% Считывает символ %

Например, %s считывает строку, a %d считывает переменную целого типа.

Строка формата считывается слева направо, при этом устанавливается соответствие между ко­дами формата и аргументами из списка аргументов.

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

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

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

Например, чтобы считать целое число и присвоить его значение переменной count, необходи­мо воспользоваться следующим обращением к scanf():

Строки считываются в массивы символов, и имя массива, без всякого указателя, является адре­сом первого элемента массива. Поэтому, чтобы считать строку в массив символов address, можно использовать команду

В этом случае имя address уже является указателем и не нуждается в префиксе &.

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

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

Знак *, помещенный после % и перед спецификатором формата, считывает данные указанного типа, но подавляет их присваивание. Таким образом, код

при вводе последовательности 10/20 присваивает значение 10 переменной х, отбрасывает символ / и присваивает значение 20 переменной у.

Командами форматирования может задаваться модификатор максимальной ширины поля. Он представляет собой целое число, которое помещается между знаком % и спецификатором фор­мата. Он ограничивает количество считываемых символов для любого поля. Например, если не­обходимо считать не больше, чем 20 символов в массив address, следует написать

scanf («%20s», address);

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

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

поместит символ х в переменную а, пробел — в переменную b и y — в переменную c.

Надо быть внимательным: любые другие символы в управляющей строке — включая пробелы, символы табуляции и новых строк — используются для указания и отбрасывания символов из входного потока. Например, при входном потоке 10t20 функция

поместит 10 в х, а 20 в у. Символ t будет отброшен, поскольку в управляющей строке имеется t.

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

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

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

С помощью кавычек можно задать диапазон воспринимаемых символов. Например, следующее выражение дает указание scanf() принимать буквы от «А» до «Z»:

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

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

При использовании Borland С++ в 16-разрядной среде можно изменить модель памяти, ис­пользуемую по умолчанию для компилирования программы, путем явного указания размера каж­дого указателя, используемого при вызове scanf(). Ближний указатель задается модификатором N, а дальний — модификатором F. (Нельзя использовать модификатор N, если программа ском­пилирована для модели памяти huge.)

Действия функции scanf ( ) в следующих примерах поясняются комментариями :
char str [ 80 ] ;
int i ;
/* чтение строки и целого */
scanf ( «%s%d» , str , & i ) ;
/* чтение до 79 символов в str */
scanf ( «%79s» , str ) ;
/* пропуск целого между двумя строками */
scanf ( » %s%*d%s» , str , & i , str ) ;

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

В то время как функция 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", c); printf("The correct is %d\n", a+b);

В данном случае, когда программа выполняется, ввод должен выглядеть примерно так: 342+1024 = 1366. Знаки «+» и » c» style=»font-family:monospace;»> 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("%[0-9]", 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() переменные не получат свои значения. Протестируйте программу.

Курс с решением части задач:
pdf-версия

scanf возвращает 2, неверно

Проблема в том, что программа работает отлично, пока я не ввожу, например, «2 13.3» В этом случае scanf все равно возвращает 2 и передает в переменную b значение 13 Однако, если ввести «13.3 2», то все работает как ожидаемо

Отслеживать
задан 2 мар 2022 в 13:31
11 1 1 бронзовый знак

@Эникейщик но ведь функция scanf возвращает количество успешно считанных значений, а не сами значения

2 мар 2022 в 13:38
да, я уже разобрался. %i — это что? целые числа это %d
2 мар 2022 в 13:39

Ну так всё верно работает. Когда функция встречает символ, который не является допустимым для ожидаемого типа, то она дальше не читает. Т.е. в первом случае она читает 1 и 3, потом натыкается на точку и решает, что данные для второго числа прочитаны т.к. точка не может встречаться в целом числе и сохраняет 13. И дальше она не сможет читать целые числа, т.к. эта точка во входном потоке не даст

Функция scanf

Для считывания данных в языке C используется функция scanf. Ее использование похоже на функцию prinf — сначала задается форматная строка, потом передаются переменные, в которые необходимо записать результат. Например, для считывания двух целых чисел функция вызывается так:

Основное отличие функции scanf в том, что при считывании чисел (или значений типа char) ей необходимо передавать адреса переменных (в языке C все параметры передаются по значению, поэтому чтобы функция scanf могла модифицировать переменную, необходимо передать в функцию адрес этой переменной). Поэтому перед названиями переменных мы пишем знак амперсанда («&»).

В функции scanf могут быть явно записаны какие-то символы, кроме форматных строк. Например, вызов

можно использовать для считывания времени, заданного в виде hh:mm — функция считает число, затем символ двоеточия, затем опять число.

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

Особенности считывания чисел

Функция scanf корректно считывает целые числа, если они начинаются с символа 0, или со знака «+». То есть числа «+123» или «0123» будут корректно считаны по форматной строке «%d», никаких дополнительных параметров задавать не нужно.

Также при считывании чисел игнорируются пробелы перед числом. Это означает, что вызов

scanf(«%d:%d», &a, &b) сможет корректно считать время, заданное в формате hh:mm при наличии пробела после двоеточия (такая запись успешно считает строки «12:34», «01:02», «01:␣23» или «␣01:␣23», поскольку дается указание считать число, затем сразу же двоеточие, затем — число, перед которым могут быть пробелы). Но такая запись не считает выражение, например, вида «01␣:␣23», поскольку после первого числа сразу должно идти двоеточие.

Чтобы считать записать вида «01␣:␣23» можно использовать форматную строку «%d :%d», причем пробел в форматной строке может означать и отсутствие пробелов.

Возможные форматные символы

Возможные форматные символы функции scanf для разных типов данных в целом соответствуют форматным символам для функции printf , но имеют меньше различных модификаторов.

Форматная
строка
Соответствующий ей тип
%hhd Считать число (десятичное) и записать его в переменную типа char (для unsigned char нужно использовать %hhu)
%hd short int (для unsigned short int нужно использовать %hu)
%d int (для unsigned int нужно использовать %u)
%ld long int (для unsigned long int нужно использовать %lu)
%lld long long int (для unsigned long long int нужно использовать %llu)
%f float
%lf double
%Lf long double
char. Считывается один символ, он может быть пробелом или символом конца строки.
%s Считывается последовательность непробельных символов (строка), записывается в C-строку (типа char * или char[])

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

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

Особенность считывания строк

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

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

Иногда бывает полезно считать всю строку целиком вместе с пробелами до конца строки. Для этого используется функция gets . Например:

Функция gets считается опасной и не рекомендуется для использования, так как она не контролирует количество считанных символов и не учитывает длину передаваемой строки, что может привести к записи данных за пределами строки.

Вместо нее рекомендуется использование функции fgets , у которой три параметра — строка для записи результата, размер строки и файловый поток, из которого читаются данные. Например:

fgets(s, 101, stdin);

В данном случае мы использовали stdin для чтения со стандартного ввода.

Не следует забывать, что в языке C в конец строки добавляется нулевой символ для обозначения конца строки. То есть если необходимо считать строку, в которой может быть 4 символа, то для нее нужно создать массив char[5] , и функции fgets нужно передавать число, не меньшее 5.

Возвращаемое значение

Функция scanf возвращает значение, равное числу успешно считанных и записанных в переданные параметры значений, что можно использовать для анализа входных данных.

Например, пусть вызвали scanf(«%d:%d», &a, &b) .

Тогда при вводе строки «12:34» функция scanf считает два числа, запишет их в переменные a и b и вернет значение 2. А при вводе «12 34» будет считано только одно число, поскольку после него должно идти двоеточие, то второе число считано не будет и функция scanf вернет значение 1.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *