Использование fflush(stdin)
Так что быстрый поиск Google для fflush(stdin)
для очистки входного буфера показывает многочисленные веб-сайты предупреждение против его использования. И все же именно так мой профессор CS учил класс делать это.
Как плохо использует fflush(stdin)
? Должен ли я действительно воздерживаться от его использования, хотя мой профессор использует его, и он, похоже, работает безупречно?
4 ответа:
простой: это неопределенное поведение, поскольку
fflush
предназначен для вызова в выходном потоке. Это выдержка из стандарта C:int fflush(FILE *ostream);
ostream указывает на выходной поток или поток обновления, в котором наиболее недавняя операция не была введена, функция функцию fflush вызывает какие-либо неписанные данные для этого потока должны быть доставлены в среду хоста для записи в файл; в противном случае поведение есть не определено.
Так что это не вопрос "насколько это плохо".
fflush(stdin)
и явно не так и не должен использовать его, никогда.
преобразование комментариев в ответ - и расширение их, так как проблема периодически появляется.
стандартный C и POSIX оставить
fflush(stdin)
как неопределенное поведениеThe POSIX C и C++ стандарты
fflush()
явно указать, что поведение не определено, но ни один из них не мешает системе определить его.ISO / IEC 9899: 2011-стандарт C11-говорит:
§7.21.5.2 функция fflush
¶2 Если
stream
указывает на выходной поток или поток обновления, в котором последняя операция не была введена,fflush
функция приводит к тому, что все неписаные данные для этого потока, которые будут доставлены в среду хоста, будут записаны в файл; в противном случае поведение не определено.POSIX в основном относится к стандарту C, но он отмечает этот текст как расширение C.
[CX] для a поток открыт для чтения, если файл еще не находится в EOF, и файл способен искать, смещение файла базового открытого описания файла должно быть установлено в положение файла потока, и любые символы, возвращенные в поток
ungetc()
илиungetwc()
, которые впоследствии не были считаны из потока, должны быть отброшены (без дальнейшего изменения файла смещение).обратите внимание, что терминалы не способны искать; ни трубы, ни розетки.
Microsoft определяет поведение
fflush(stdin)
Microsoft и среда выполнения Visual Studio определяет определение поведения
fflush()
на входном потоке.если поток открыт для ввода,
fflush
очищает содержимое буфера.Cygwin является примером довольно распространенной платформы, на которой
fflush(stdin)
не очищает вход.вот почему этот ответ версия моя комментарий Примечания "Microsoft и среда выполнения Visual Studio" -Если вы используете библиотеку времени выполнения не Microsoft C, поведение, которое вы видите, зависит от этой библиотеки.
документация и практика Linux, похоже, противоречат друг другу
удивительно, Linux номинально документирует поведение
fflush(stdin)
тоже, и даже определяет его таким же образом (Чудо творивший чудеса.)для потоков ввода,
fflush()
удаляет все буферизованные данные, которые были извлечены из базового файла, но не были использованы приложением.я по-прежнему немного озадачен и удивлен документацией Linux, в которой говорится, что
fflush(stdin)
будет работать. Несмотря на это предложение, он обычно не работает на Linux. Я только что проверил документацию на Ubuntu 14.04 LTS; в ней говорится, что указано выше, но эмпирически это не так работа-по крайней мере, когда входной поток не является искомым устройством, таким как терминал.
demo-fflush.c
#include <stdio.h> int main(void) { int c; if ((c = getchar()) != EOF) { printf("Got %c; enter some new data\n", c); fflush(stdin); } if ((c = getchar()) != EOF) printf("Got %c\n", c); return 0; }
пример вывода
$ ./demo-fflush Alliteration Got A; enter some new data Got l $
этот вывод был получен как на Ubuntu 14.04 LTS, так и на Mac OS X 10.11.2. Насколько я понимаю, это противоречит тому, что говорится в руководстве Linux. Если
fflush(stdin)
операция работала, мне нужно было бы ввести новую строку текста, чтобы получить информацию для второгоgetchar()
читать.учитывая, что стандарт POSIX говорит, Может быть, нужна лучшая демонстрация, и документация Linux должна быть уточнена.
demo-fflush2.c
#include <stdio.h> int main(void) { int c; if ((c = getchar()) != EOF) { printf("Got %c\n", c); ungetc('B', stdin); ungetc('Z', stdin); if ((c = getchar()) == EOF) { fprintf(stderr, "Huh?!\n"); return 1; } printf("Got %c after ungetc()\n", c); fflush(stdin); } if ((c = getchar()) != EOF) printf("Got %c\n", c); return 0; }
пример вывода
обратите внимание, что
/etc/passwd
это искомый файл. На Ubuntu, первая строка выглядит так:root:x:0:0:root:/root:/bin/bash
на Mac OS X первые 4 строки выглядят так:
## # User Database # # Note that this file is consulted directly only when the system is running
другими словами, есть комментарий в верхней части Mac OS X . Строки без комментариев соответствуют нормальному макету, так что
root
запись:root:*:0:0:System Administrator:/var/root:/bin/sh
Ubuntu 14.04 LTS:
$ ./demo-fflush2 < /etc/passwd Got r Got Z after ungetc() Got o $ ./demo-fflush2 Allotrope Got A Got Z after ungetc() Got B $
Mac OS X 10.11.2:
$ ./demo-fflush2 < /etc/passwd Got # Got Z after ungetc() Got B $
поведение Mac OS X игнорирует (или, по крайней мере, кажется, игнорирует)
fflush(stdin)
(таким образом, не следуя POSIX по этому вопросу). Поведение Linux соответствует документированному поведению POSIX, но спецификация POSIX гораздо более осторожна в том, что она говорит - она указывает файл, способный искать, но терминалы, конечно же, не поддерживают поиск. Это также гораздо менее полезно, чем спецификация Microsoft.резюме
Microsoft документирует поведение
fflush(stdin)
. По-видимому, он работает так, как описано на платформе Windows, используя собственный компилятор Windows и библиотеки поддержки среды выполнения C.несмотря на противоположную документацию, он не работает в Linux, когда стандартный ввод является терминалом, но, похоже, следует спецификации POSIX, которая гораздо более тщательно сформулирована. В соответствии с стандарт C, поведение
fflush(stdin)
неопределено. POSIX добавляет квалификатор "если входной файл не доступен для поиска", что не является терминалом. Поведение-это не то же самое, что и Microsoft.следовательно, портативный код не использует
fflush(stdin)
. Код, привязанный к платформе Microsoft, может использовать его, и он будет работать, но остерегайтесь проблем с переносимостью.POSIX способ отбросить непрочитанный ввод терминала из файлового дескриптора
стандартный способ POSIX отбросить непрочитанную информацию из дескриптора файла терминала (в отличие от потока файлов типа
stdin
) иллюстрируется на как я могу очистить непрочитанные данные из очереди ввода tty в системе Unix. Однако это работает ниже стандартного уровня библиотеки ввода-вывода.
согласно стандарту,
fflush
может использоваться только с выходными буферами, и, очевидно,stdin
не один. Однако,некоторые компиляторы обеспечивают использование fflush (stdin) в качестве расширения. В этом случае вы можете использовать его, но это повлияет на переносимость, поэтому вы больше не сможете использовать любой совместимый со стандартами компилятор на земле и ожидать тех же результатов.
цитата POSIX:
для потока, открытого для чтения, если файл еще не находится в EOF, а файл один способный к поиску, смещение файла базового открытого описания файла должно быть установлено к положению файла потока, и все символы нажатые назад на поток мимо ungetc () или ungetwc (), которые впоследствии не были считаны из потока, должны быть dis- карточку (без дальнейшего изменения файла смещение.)
обратите внимание, что терминал не способен искать.