Использование fflush (stdin)

Поэтому быстрый поиск Google для fflush(stdin) для очистки входного буфера показывает многочисленные веб-сайты, предупреждающие об их использовании. И все же именно это мой преподаватель CS преподавал этому classу.

Насколько плохо используется fflush(stdin) ? Должен ли я действительно воздерживаться от его использования, хотя мой профессор использует его, и он работает безупречно?

Простой: это неопределенное поведение, поскольку fflush предназначен для fflush в выходном streamе. Это выдержка из стандарта C:

int fflush (FILE * ostream);

ostream указывает на выходной stream или stream обновления, в котором последняя операция не была введена, функция fflush приводит к тому, что любые неписанные данные для этого streamа должны быть доставлены в среду хоста для записи в файл; в противном случае поведение не определено.

Так что это не вопрос «насколько это плохо». fflush(stdin) явно неправильный , и вы не должны его использовать .

Преобразование комментариев в ответ – и расширение их с момента появления вопроса периодически.

Стандартные C и POSIX оставляют fflush(stdin) как неопределенное поведение

Стандарты POSIX , C и C ++ для fflush() явно fflush() что поведение не определено, но ни один из них не позволяет системе определить его.

ISO / IEC 9899: 2011 – стандарт C11 – говорит:

§7.21.5.2 Функция fflush

¶2 Если stream указывает на выходной stream или stream обновлений, в котором последняя операция не была fflush функция fflush заставляет любые неписаные данные для этого streamа доставляться в среду хоста, которая должна быть записана в файл; в противном случае поведение не определено.

POSIX в основном отказывается от стандарта C, но он отмечает этот текст как расширение C.

[CX] Для streamа, открытого для чтения, если файл еще не находится в EOF, и файл способен искать, смещение файла описания открытого файла должно быть установлено в положение файла streamа, а любое символы, отодвинутые назад в stream через ungetc() или ungetwc() , которые впоследствии не были прочитаны из streamа, должны быть отброшены (без дальнейшего изменения смещения файла).

Обратите внимание, что терминалы не способны искать; ни трубы, ни розетки.

Microsoft определяет поведение fflush(stdin)

Microsoft и среда исполнения Visual Studio определяют определение поведения fflush() во входном streamе.

Если stream открыт для ввода, fflush очищает содержимое буфера.

Примечания MM :

Cygwin является примером довольно распространенной платформы, на которой fflush(stdin) не очищает входные данные.

Вот почему в этом ответе на версию моего комментария «Microsoft и среда выполнения Visual Studio» – если вы используете библиотеку времени выполнения, отличную от Microsoft C, поведение, которое вы видите, зависит от этой библиотеки.

Документация и практика Linux, похоже, противоречат друг другу

Удивительно, но Linux номинально документирует поведение fflush(stdin) тоже, и даже определяет его так же (чудо чудес).

Для входных streamов fflush() отбрасывает любые буферизованные данные, которые были извлечены из основного файла, но не были использованы приложением.

Я немного озадачен и удивлен документацией Linux, в которой говорится, что fflush(stdin) будет работать. Несмотря на это предложение, он обычно не работает в Linux. Я только что проверил документацию по Ubuntu 14.04 LTS; он говорит то, что цитируется выше, но эмпирически это не работает – по крайней мере, когда входной stream является недоступным для поиска устройством, таким как терминал.

demo-fflush.c

 #include  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  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 /etc/passwd . Строки без комментариев соответствуют нормальному макету, поэтому 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 отбрасывать непрочитанную информацию из дескриптора файла терминала (в отличие от streamа файлов, такого как stdin ), проиллюстрирован на странице Как я могу сбросить непрочитанные данные из очереди ввода tty в системе Unix . Тем не менее, это работает ниже стандартного уровня библиотеки ввода-вывода.

Согласно стандарту, fflush может использоваться только с выходными буферами, и, очевидно, stdin не один. Однако некоторые компиляторы обеспечивают использование fflush (stdin) в качестве расширения. В этом случае вы можете использовать его, но это повлияет на переносимость, поэтому вы больше не сможете использовать какой-либо совместимый со стандартами компилятор на земле и ожидать тех же результатов.

Цитата из POSIX :

Для streamа, открытого для чтения, если файл еще не находится в EOF, и файл способен искать, смещение файла описания открытого файла должно быть установлено в положение файла streamа, а любые символы, отбрасываемые назад в stream через ungetc () или ungetwc (), которые впоследствии не были прочитаны из streamа, должны быть удалены (без дальнейшего изменения смещения файла).

Обратите внимание, что терминал не способен искать.

  • Сравнить два дерева каталогов
  • Как прочитать строку, введенную пользователем в C?
  • Как определить, была ли перенаправлена ​​Console.In (stdin)?
  • Использование fseek с указателем файла, указывающим на stdin
  • Очистить stdin перед чтением
  • запустить exe / process с stdin stdout и stderr?
  • Я не могу сбросить stdin
  • setvbuf не может сделать stdin небуферизованным
  • Чтение Eclipse stdin (System.in) из файла
  • Чтение из stdin flush stdout?
  • Можно ли установить тайм-аут для std :: cin?
  • Давайте будем гением компьютера.