Перейти к содержимому

Зачем в scanf амперсанд

  • автор:

Функция 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.

Для чего нужен амперсанд в параметре функции?

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

27 фев 2020 в 14:01

Я в С и C++ не силён, поэтому просто уточню: разве когда мы передаём переменную в функцию через указатель, не остаётся ли у нас возможность получать доступ к значению аргумента и изменять его?

Ввод данных в языке Си. Функция scanf

Основная задача этого шага научиться получить данные от пользователя.

Для этого можно использовать функцию scanf . Она, так же как и функция printf , описана в заголовочном файле stdio.h . Т.к. он у нас уже подключен в первой строке, то мы можем свободно использовать функцию scanf в своей программе.

Чтобы считать данные, которые ввёл пользователь, и сохранить их в переменную, нужно вызвать функцию scanf со следующими параметрами:

Общий синтаксис функции scanf

Рис.1. Общий синтаксис функции scanf.

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

Основные спецификаторы формата:

%d — целые числа
%f — вещественное число типа float
%lf — вещественное число типа double (обозначение lf от long float)
%c — символ

Обратите внимание, что в функции scanf для типов float и double используются различные спецификаторы формата.

После формат-строки нужно указать адрес переменной, в которую нужно сохранить данные. Чтобы указать адрес переменной достаточно перед её именем записать знак & (амперсанд), как на рисунке выше.

Напишем, наконец-таки, программу сложения двух чисел.

#include int main(void) < int a, b, res; scanf("%d", &a); // считываем целое значение в переменную a scanf("%d", &b); // считываем целое значение в переменную b res = a + b; printf("%d + %d = %d\n", a, b, res); return 0; >

Скомпилируйте и запустите эту программу. После того, как программа запущена она будет ждать пока вы введёте данные. Мы с вами знаем какие данные нужно вводить, т.к. мы писали эту программу. Для других пользователей, которые код программы не увидят, хорошо бы вывести на экран подсказку, какие данные нужно ввести. Посмотрите как это реализовано в следующем примере.

Эта программа будет полезна тем, кто следит за своим здоровьем. Данная программа подсчитывает уроверь базового обмена веществ по формуле Миффлина-Сан Жеора исходя из данных, которые вы введёте (возраст, рост и вес).

#include int main(void) < int age, height, weight; double bov_m, bov_f; printf("Vash vozrast?(god)\n"); scanf("%d", &age); // считываем целое значение в переменную age printf("Vash rost?(cm)\n"); scanf("%d", &height); // считываем значение в переменную height printf("Vash ves?(kg)\n"); scanf("%d", &weight); // считываем значение в переменную weight bov_m = 10*weight + 6.25*height - 5*age + 5; bov_f = 10*weight + 6.25*height - 5*age - 161; printf("| BMR |\n"); printf("| male | female |\n"); printf("|%8.2f|%8.2f|\n",bov_m, bov_f); return 0; >

На самом деле, функция scanf – функция форматного ввода. Она устроена чуть ли не сложнее, чем printf . Но рассказывать новичку об особенностях её работы значит понапрасну грузить его лишней информацией. На данном этапе вам будет достаточно и этих знаний, а когда появится необходимость использовать что-то большее, вы с этим сможете сами разобраться. А может и я в каком-нибудь уроке расскажу.

Сохрани в закладки или поддержи проект.

Практика

Решите предложенные задачи. Для удобства работы сразу переходите в полноэкранный режим

Исследовательские задачи для хакеров:

  1. Попробуйте удалить в программе Листинг 2 символ & в какой-нибудь функции scanf . Посмотрите какую ошибку выдаст компилятор.
  2. Попробуйте использовать %f для типа double , а %lf для типа float . Каковы результаты? Напишите об этом в комментариях к этому уроку. Не забудьте указать название вашей IDE или компилятора.

Дополнительные материалы

  1. пока нет

Самый крупный донат:

sdanshin@yandex.ru 5000 руб.
Аноним, Юmoney * 9482 1960 руб.
Руслан Викторович П, +7(985) ***-**-99 1000 руб.
Сергей Евгеньевич С, +7 (977) ***-**-40 500 руб.

Оставить комментарий

Чтобы код красиво отображался на странице заключайте его в теги [code] здесь писать код [/code]

Комментарии

Сергей 09.12.2017 в 14:58

Здраствуйте мне нужна помощь :когда я задаю значение float он в ответе пишет так : 3.50 + 3.40 = 6.9000000
Как убрать нолики?

KaDeaT

Об этом рассказывается в уроке Форматированный вывод.

Посмотрите на листинг 8 и на результат вывода.

Дмитрий 15.12.2017 в 00:09

Нет возможности в некоторых темах сохранить урок ввиде PDF файла для изучения в offline когда летишь в самолёте! Создайте пожалуйста одну общую кнопку для сохранения уроков в режиме ,offline пожалуйста

KaDeaT

Дмитрий, добрый день. Да, знаю об этой проблеме.

К сожалению не хватает времени сделать pdf-версии для всех уроков. Но оно обязательно будет, обещаю. Не могу быть уверенным, что к концу этого года успею, но уж в январе 2018 pdf-версия будет у каждого урока.

Кирилл 03.01.2018 в 20:18

Здравствуйте, появился вопрос: почему при выводе переменных в printf мы не используем знак «&», хотя в вводе scanf он необходим?
Заранее большое спасибо

KaDeaT

Добрый день.
Это одна из особенностей того, как устроены функции в языке Си.

Функции не могут изменять те переменные, которые им передаются. Чтобы они могли изменять эти переменные, надо им передавать не переменную, а адрес в памяти где хранится переменная. Поэтому для printf, которая просто выводит переменную на экран не изменяя её, нужно передавать только имя переменной, а для scanf, которая записывает в переменную данные, тем самым изменяя её, нужно передавать адрес в памяти.
Чтобы получить адрес переменной, нужно использовать операцию &.

DimaKotoriyRAshid 08.01.2018 в 23:32

У меня никак не получается сделать предпоследнее задание , постоянно выдает ошибку номер 2. При правильном ответе он пишет какую то откровенную дич. Уже битый час пытаюсь найти ошибку ((9(

KaDeaT
Пожалуйста, напишите в комментариях на Stepik номер своего решения. Попробую вам помочь.)
Андрей 21.03.2018 в 21:16

«но уж в январе 2018 pdf-версия будет у каждого урока» это цитата из комментария))
Я не могу найти эту кнопку.

KaDeaT

Дела и заботы, к сожалению, помешали сбыться этому плану. Никак не могу взяться за эту работу. Но возьмусь летом, а может даже раньше.

Farid 28.06.2018 в 17:09
ya reshil vse zadaci,pishu kuski sekretnogo klyuca,no mne pishut neverniy klyuc.mojete proveryat?
KaDeaT
Проверил, всё работает правильно. Может быть вы не удалили пробелы? Пробелов быть не должно.
Виталий 07.08.2018 в 04:48

* а когда появится необходимость использовать что-то большее, вы с этими сможете сами разобраться — очепятка ~вы с ЭТИМ сможете~

KaDeaT
Спасибо. Поправил!
Samvel 26.08.2018 в 07:40
1 + -365906032 = 803628530
Eclipse
KaDeaT
Николай Евсюков 27.08.2018 в 18:26

1. CRT: unhandled exception (main) — terminating
*** Process returned 255 ***

2. float f;
double d;
scanf(«%lf», &f);
scanf(«%f», &d);
printf(«%f %f», f, d);

Ввод:
1.1
2.2
Вывод:
-0.000000 0.000000

P.S. IDE Pelles C.

KaDeaT
Krowka_v_Kartowke 05.11.2018 в 23:59

Здравствуйте, нашел опечатку. «Скомпилируйте и запустить(*те) эту программу.». Урок отличный, спасибо Вам.

KaDeaT
Спасибо за внимательность! Поправил.
не_робот_я 17.02.2019 в 11:27

хакерские задачи:
1. сегфолт (ошибка сегментации)
2. Xcode ругается так «Format specifies type ‘float *’ but the argument has type ‘double *'», а онлайн компилятор onlinegdb.com не ругается, но результат выдаёт нулевой.

KaDeaT
Estel 05.03.2019 в 15:16

Отличные уроки, большое спасибо!

по заданию 1:
Variable ‘dollars’ is uninitialized when used here
Initialize the variable ‘dollars’ to silence this warning
по заданию 2:
Format specifies type ‘float *’ but the argument has type ‘double *’
Format specifies type ‘double *’ but the argument has type ‘float *’
(Xcode)

KaDeaT
Максим 14.04.2019 в 15:38

Использую Dev C++.
1. Если удалить адрес из какой-либо переменной, она на выводится на экран, при нажатии на клавишу.
2. Если использовать спецификатор %f для типа double, выводимый результат будет 0.00000+0.00000=0.00000.
3. Если использовать спецификатор %lf для типа floai, второе слагаемое будет всегда 0.00000, а первое будет выдавать неверно введенные данные.

pchristi 02.07.2019 в 08:35

Поэкспериментировал со второй хакерской задачкой. Вводил число 3.14 потом 1.12;
В первом случае при %f и double в printf был такой результат:
1) 512181179925964090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000.000000
2) 512181179185108140000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000.000000
Во втором случае, при %lf и типе float вышло так:
1) 126443839488.000000
2) 0.000000

Александр 30.09.2019 в 12:27

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

Зачем в scanf амперсанд

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

Например, в турбо-паскале есть процедура inc. Она каждый раз при вызове увеличивает значение первого аргумента на второй аргумент (если второй аргумент не задан, предполагается его равенство 1).

На C/C++ аналогичную функцию выполняет оператор ++ (например, i++). Можно реализовать эту операцию с помощью функции, которую напишем сами с примененм символа &

int inc(int &a) ;

Если мы обратимся к этой функции, то после её применения аргумент будет инкрементирован (увеличен на 1).

Вадим Мошев
Посмотреть профиль
Найти ещё сообщения от Вадим Мошев

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

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