Функция printf() обеспечивает вывод данных с заданным форматированием. Аналогично, функция 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 отсутствует знак амперсанда. Это не ошибка. В дальнейшем вы узнаете, что имя массива уже является адресом начала массива.

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

Для целых чисел: длинное целое - %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\nThe 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("%[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(), но данные остаются в буфере и будут пытаться считываться при последующих вызовах. Поэтому стоит предусмотреть очистку буфера в случае некорректного ввода:

  if (scanf("%d", &a) != 1)
    scanf("%*s");
  scanf("%f", &b);

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

Потенциально мощная функция scanf() также несет ряд трудностей и затрат.

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

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

Вопросы для самопроверки:

  1. Каково основное предназначение `scanf()` в языке программирования C?
  2. Почему при использовании `scanf()` необходимо передавать адреса переменных, а не их значения?
  3. Перечислите спецификаторы формата, которые можно использовать с `scanf()` для считывания целых чисел и вещественных чисел.
  4. Как `scanf()` обрабатывает пробелы в строке формата?
  5. Что произойдет, если данные введены некорректно в `scanf()`, например, строка для переменной целого типа?
  6. Как использовать `scanf()` для игнорирования части вводимых данных?
  7. В чем заключается использование форматов […] и [^…] в `scanf()`?

Программа курса:

  1. Описание курса
  2. Введение в язык программирования C
  3. Типы данных в C и форматированный вывод
  4. Символьные типы и управляющие символы в C
  5. Операторы ветвления и switch в C
  6. Циклы и операторы в языке C
  7. Битовые операции в языке C
  8. Посимвольный ввод и вывод в C - буферизация
  9. Переменные, адреса и указатели в C
  10. Передача аргументов по ссылке и значению в C
  11. Форматированный ввод данных с использованием scanf
  12. Генерация псевдослучайных чисел на C
  13. Адресная арифметика в массивах C
  14. Передача массивов в функции и указатели
  15. Строки в языке C - особенности и функции работы
  16. Функции работы со строками в C
  17. Работа со структурами в C - создание и применение
  18. Динамические структуры данных в C
  19. Ввод и вывод данных из файлов в языке C
  20. Передача аргументов в C и работа с файлами
  21. Препроцессор в языке C - директивы и макросы
  22. Создание и компиляция многофайловых программ в C
  23. Использование статических и динамических библиотек в C