#9. Функция scanf() для форматированного ввода | Язык C для начинающих
Vložit
- čas přidán 11. 09. 2024
- Практический курс по C/C++: stepik.org/cou...
Телеграм-канал: t.me/java_and_c
Инфо-сайт: proproprogs.ru...
Порядок работы функции scanf() форматированного ввода данных из входного потока stdin. Спецификаторы преобразований и модификаторы.
Самый лучший преподаватель, которого я когда-либо слышал. Ваши курс на Stepik по ООП - это excellence! Спасибо Вам огромное!
Спасибо за уроки, очень толковые, всё по делу и без воды. Время 18:00, для модификатора "l" я полагаю приведена ошибочная строка "%hx, %ho, %hu - для unsigned long;", нужно "%lx, %lo, %lu ...
Отличное изложение, спасибо за материал!
Отличный плейлист.По Си не так много видосов.Автору респект👍.
Новый урок, спасибо большое. Всегда жду с нетерпением нового выпуска
Сергей, как же хорошо, что Вы вернулись!) Давно от Вас не было видео
Спасибо. Самое ценное на последних секундах.
Большое спасибо за курс! Отличное и понятное изложение.
Ура, новая лекция!!! Сергей, вы заставили нас волноваться))) Мы уже привыкли к регулярным выходам новых видео, а тут...(( получился большой перерыв после предыдущего видео по Си... Шучу, конечно же...Огромнейшая вам благодарность за то, что находите время делать такие крутые лекции!!!
P.S. Отдельное спасибо за сертификат с отличием по курсу Добрые, добрые структуры данных! Это приятная неожиданность. Уверен, что курс будет востребован (свой комментарий на Stepik я написал ;))
Спасибо за видео! Форматированный ввод - очень удобная вещь.
Спасибо большое за прекрасно изложенный материал. Все просто супер.
Сергей, если вдруг планируете вернуться к изложению материала на Python - Flask and Django - представлены на 10 баллов из 10. Хотелось бы еще увидеть такой материал на вашом канале - фреймворк FastAPI - сейчас он набирает полулярность космическими темпами. На ютубе есть пару материалов, но это не то, кроме вас никто не сможет отобразить все основные моменты данного фреймворка)))
Как раз начал изучать Си. Спасибо большое!!!
Чем спецификатор формата отличается от модификатора формата? Надо бы про это рассказать. Или дать ссылку на стандарт, а то путаница возникает.
Спасибо Вам! очень интересно!
Спасибо огромное. Отличный урок
Спасибо
👏👍
Почему scanf() не работает в режиме отладки?
Топчик
Сергей, приветствую! скажите, а канал Network Education случайно не ваш ?) голос очень похож)
нет )
@@selfedu_rus следующий вопрос) кокую книгу по с++ посоветуете из лафоре и прата ?? )
#include
int main()
{
int s;
int r1, r2, r3, res;
scanf("%d", &s);
r1 = s / 100;
r2 = (s / 10)%10;
r3 = s % 10;
res = r1 + r2 + r3;
printf("%d", res);
return 0
}
Запустил в режиме отладки, останавливается на вызове функции scanf(); вот только в терминале уже не могу ввести данные.
Без отладки все работает исправно. Как при отладке работать с scanf(); ?
Выполняю уроки в разных ОС - Win и Linux(Ubuntu) в одной и той же среде VSCode (настройки одинаковые std=c99).
Немного отличается результат при компиляции. В строке 8, в Linux выдается warning:
...use of assignment suppresion and length modifier together in gnu_scanf format...
,но ввожу данные и всё отрабатывает, как положено. На Win такого нет! Поискал в Google, есть сообщения о таком, но что это и как исправить пока не понимаю, дальше будем разбираться, очень интересно!
И все же не совсем понимаю, поясните пожалуйста в чем логическая ошибка:
char x; // объявление переменной - операционная система где то выделяет память. На данном этапе появляется информация об адресе ячейки и длине зарезервированной памяти в одну ячейку.
x = 5; число 5 записывается в переменную х. И вот после просмотра лекции я уже не понимаю что делает этот оператор. Гораздо логичнее будет так:
&x = 5; (или 00000101); при этом мы обращается к адресу ячейки и заносим туда значение.
Так почему мы заносим значение по имени а не сразу по адресу как сканф?
Имя переменной - это абстракция языка Си (равно, как и всех других ЯП высокого уровня). Когда компилятор видит имя x он понимает, что в выделенную по эту переменную область памяти, нужно занести (записать) число 5 и формирует соответствующие машинные коды.
ахренетья даже и не знал что * делает пропуск данных
Здравствуйте! Подскажите, пожалуйста, как пофиксить проблему "Code is already running"?
в трее запущен exe-файл и не завершается, удалите его вручную
16:14 "...возвращаемся в программу, запускаем её..." и только у меня одного ничего не запускается, потому что компилятор выдаёт ошибку "format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘long long int *’ [-Wformat=]" ??
Вполне возможно, что у вас компилятор проверяет строгое соответствие спецификатора и типа переменной. В scanf() вместо %d пропишите %lld (компилятор должен отрабатывать по стандарту C99).
я конечно дико извиняюсь, но кто решил что будет классной идеей сделать приведение %f в float для scanf(), но в double для printf()?
в printf() и float и double выводятся как тип double; в scanf() такое сделать нельзя, т.к. размеры типов разные и нужны уже два спецификатора
Непойму, почему в scanf используются адреса переменных, а не сами переменные?
если прописать саму переменную, то данные из нее просто будут скопированы в соответствующий параметр функции scanf и все
@@selfedu_rus а, вот оно что, получается разработчики попытались вложить смысл в каждую возможную вариацию синтаксиса.. сложнааа)
Потому что объявления какого-нибудь параметра в функции попадает в стек. А при выходя из области видимости переменная которая была там объявлена удаляется в стеке. То есть, параметр к примеру foo в функции just_function, при достижения компилятором конечной фигурной скобки этой функции, удаляет все объявленные переменные, параметры, из стека. Потому вводимые переменные при вызове функции ничего не меняют, функция лишь копирует значения переменной которую ввели в вызове и присваивает параметру, потом же эти переменные удаляются из стека. Чтобы это не произошло, есть 2 варианта:
1) Выделить память в куче
2) Сделать переменную статистической (у статистической переменной продолжительность хранения является окончания программы, то есть она будет жить в оперативной памяти пока программа не завершится).
Но, это все слишком уж муторно, к примеру тебе надо просто изменить значения переменной в функции, тебе что обязательно выделять память в куче или объявлять статистическую переменную? Нет. Можно сделать все проще. Можно использовать указатель (указатель это переменная которая хранит адрес другой переменной).
В функции scanf, первый параметр как раз таки и является указатель. Зачем в этой функции указатель если значения его адрес другой переменной? Ну потому что указатель можно разыменовать, что такое разыменование? Это возвращение значения переменной, у которой адрес ты как раз таки хранишь. Разыменования в указателях обозначается символом *. Я приведу пример:
int i = 2;
int *ptr = &i; // &i - это адрес переменной i, указатель ptr хранит адрес переменной i
printf("%d", *ptr); // выведет: *ptr == 2.
Так вот. В функциях если параметр является указателям, ты можешь там же изменять значения переменной у которой адрес ты хранишь в указателе, с помощью разыменования. И даже не надо будет беспокоиться об стеке, потому что после окончания функции твой параметр указатель просто исчезнет из стека, но это не меняет то что ты изменил значения переменной у которой адрес ты хранишь.
void scn(int *ptr) {
*ptr = 4;
}
int main() {
int i = 2;
printf("Var I: %d", i); // Var I: 2
scn(&i); // я вызываю функцию scn и передаю адрес переменной
printf("Var I: %d", i); // Var I: 4
}
У процессоров есть регистры, в них могут храниться значения, либо адреса памяти по которым эти значения находятся. Также у процессоров есть инструкции, которые могут принимать и возвращать значения в регистрах, адреса в регистрах, значения адресов находятся в памяти. И то и другое просто последовательность нулей и/или единиц, даже сами инструкции.
Допустим у нас есть функция на Си для перемещения значений двух переменных между собой, через третью:
long exchange(long *xp, long y)
{
long x = *xp;
*xp = y;
return x;
}
На языке ассемблера получится следующее:
(адрес xp хранится в регистре %rdi, значение y в %rsi)
1 movq (%rdi), %rax перемещает значение по адресу находящемуся в регистре %rdi в регистр %rax
2 movq %rsi, (%rdi) перемещает значение из регистра %rsi по адресу хранящемуся в регистре %rdi
ret
Первая инструкция считывает значение x из памяти и сохраняет его в регистре %rax, что является прямой реализацией операции x = *xp, позже регистр %rax будет использован для возврата значения из функции, и поэтому возвращающим значением будет x. Вторая инструкция записывает значение y в ячейку памяти с адресом xp, что является реализацией *xp = y. Этот пример иллюстрирует как инструкцию mov можно использовать для чтения из памяти в регистр и для записи из регистра в память. Во-первых мы видим то что называется указателями в Си - это просто адреса. Во-вторых, локальные переменные, такие как x, часто хранятся в регистрах, а не в ячейках памяти. Доступ к регистру происходит намного быстрее, чем доступ к памяти.
В языке С аргументы функции передаются по значению. Поэтому указание самой переменной передает в функцию то, что содержится в переменной (например ,если в переменной записано число 3, то функция получит просто 3 ). Но чтобы изменить значение переменной, функция должна записать что-то в ту область памяти, которая отведена для этой переменной, поэтому ей нужно передать значение адреса, начиная с которого располагается данная переменная (а длина этой области определяется буквой спецификатора после %).
И довольно частая ошибка начинающих - забыть & для таких переменных. В С++ добавлено более удобное средство: там можно в заголовке функции указать у таких переменных &, тогда при вызове можно будет просто указывать саму переменную, без &. Но scanf - это древняя функция из первоначального С, где такой возможности не было, поэтому для совместимости оставлено именно так.
Здравствуйте, подскажите пожалуйста как собирать и компилировать многофайловые проекты VS Code...буду очень благодарен!!!