Как \n влияет на сканирование строк в scanf?

Вот простой пример: у меня есть массив из 3 символов, я пишу ввод на терминале и хочу немедленно проверить, что я отсканировал, например:

scanf("%3s\n",array);
printf("%s",array);

Что я хочу знать, так это: почему после запуска программы, если я пишу на терминале «abc», чтобы заставить ее распечатать массив, мне нужно ввести другой ввод, например, написать другую букву или число?

Если я напишу «abcd», а затем нажму ENTER, он сразу же распечатается, но если я просто напишу «abc» и нажму ENTER много раз, он все равно не перейдет к следующей инструкции (то есть printf).

Я знаю, что это связано с тем, как \n заставляет scanf читать строку, но я не совсем понимаю.


person wattbatt    schedule 31.07.2018    source источник
comment
как вы объявили array? если он имеет только 3 элемента, он не может хранить 3-символьную строку, и вы получаете UB   -  person phuclv    schedule 31.07.2018
comment
Вы пытались изменить %3 на %2 в своей функции scanf()?   -  person Uclydde    schedule 31.07.2018
comment
@JBraha нет, не делал, разве %2s не сканирует только 2 символа? О, правильно, нет места для терминатора, если я сделаю %3s, правильно?   -  person wattbatt    schedule 31.07.2018
comment
@wattbatt Что ж, если в вашем коде %3 и «abcd» (строка из 4 символов) работали правильно, я подумал, что %2 и «abc» (строка из 3 символов) могут работать правильно.   -  person Uclydde    schedule 31.07.2018


Ответы (1)


Дополнительный ввод требуется не для printf (как предполагалось в первой версии вашего теперь отредактированного заголовка), а для scanf.
Ваша строка формата содержит "\n".
Это пробел.< br> Любые пробелы в этой позиции сообщат scanf, что необходимо потреблять любое количество пробелов, следующих за тремя символами (например, "abc").
Пока вы продолжаете добавлять пробелы (включая возвраты), scanf не завершает использование "всех символов". после пробела".
Как только вы введете любой непробельный символ (например, "d"), он узнает "ага, все пробелы сделаны". Тогда, и не раньше, он может завершиться. Оставляет непробельные символы во входном потоке и возвращает значение.
Обратите внимание (спасибо chux), что при строковом буферизованном вводе (как правило, почти всегда, если вы не изменяли это намеренно) вам необходимо ввести такой непробельный символ с возвратом/новой строкой, следующим за ним где-то.

Обратите внимание, как упоминалось в комментариях, вам нужно 4 символа пробела в целевом массиве, чтобы также разместить всегда написанный «\ 0» в конце строки. Если у вас есть только три символа, то последний 0 будет записан за пределами неопределенного поведения.

Я не уверен, как решить вашу проблему, потому что я не могу сказать, какова цель вашего "\n". Но я думаю, вам следует попробовать поведение, если вы переместите "\n" из scanf() и, возможно, в printf(). Следующий вызов scanf(), начинающийся со спецификатора преобразования (кроме "[", "c" и "n"), в любом случае использует все начальные пробелы, включая все оставшиеся переводы строк/возвраты из этого.

person Yunnosch    schedule 31.07.2018
comment
Как только вы вводите любое непробельное пространство (например, d), он знает, ага, все пробелы заполнены. Тогда, и не раньше, он может завершиться. --› Возможно, не скоро, поскольку stdin обычно буферизуется строкой, и поэтому требуется следующая строка ввода, прежде чем scanf() увидит какие-либо данные. - person chux - Reinstate Monica; 31.07.2018
comment
@chux Хороший вопрос. Я улучшил формулировку. Спасибо за отзыв. - person Yunnosch; 31.07.2018
comment
До сих пор я работал со строками как с массивами символов, поэтому я не знал о символе конца строки. По этому поводу у меня есть дополнительный вопрос: мой массив char array[3];, значит, в нем есть место для 3 символов, верно? Почему, если я пишу abc, он печатается регулярно и не выдает никаких ошибок, так как для символа «\ 0» больше нет места? - person wattbatt; 31.07.2018
comment
См. цитату en.cppreference.com/w/c/io/fscanf Всегда сохраняет нулевой символ в дополнение к совпадающим символам (поэтому в массиве аргументов должно быть место как минимум для символов ширины + 1). Обратите внимание, что scanf не знает о размере того, что вы называете местом назначения. - person Yunnosch; 31.07.2018
comment
@federicomasseroni C не указывает, чтобы выдавать ошибку, когда ваш код пишет за пределами array[3]. Код нарушил правила, так что остальная часть кода спорна. поскольку для «\ 0» больше нет места, неизвестно, верно ли это. Мы знаем, что определенно недостаточно места - может быть - может быть недостаточно места - все это неопределенное поведение. - person chux - Reinstate Monica; 31.07.2018
comment
Я знаю, что не должен обращаться к избирателям. Но, тем не менее, этот четвертый голос присудил мне серебряную тройку. Большое вам спасибо. :-) - person Yunnosch; 31.07.2018