Использование fflush(stdin)
Так быстрый поиск Google для fflush(stdin)
для очистки входного буфера показывает многочисленные веб-сайты предупреждение против его использования. И все же именно так мой профессор CS учил класс делать это.
Как плохо использует fflush(stdin)
? Должен ли я действительно воздерживаться от его использования, даже если мой профессор использует его и, похоже, работает безупречно?
4 ответов
Simple: это неопределенное поведение, так как fflush
предназначен для вызова в выходном потоке. Это выдержка из стандарта C:
int fflush(FILE *ostream);
ostream указывает на выходной поток или поток обновлений, в котором больше всего недавняя операция не была входной, функция fflush вызывает любые неписаные данные для этого потока, который будет доставлен к среде должно быть написано в файл; в противном случае поведение есть не определено.
Так что это не вопрос" насколько плохо " это. fflush(stdin)
is явно неправильно и нельзя им пользоваться, никогда!--12-->.
преобразование комментариев в ответ - и расширение их, так как проблема периодически появляется.
стандартный C и POSIX оставить fflush(stdin)
как неопределенное поведение
на POSIX C и C++ стандарты fflush()
явно указать, что поведение не определено, но ни один из них не мешает системе определить его.
ISO / IEC 9899: 2011-стандарт C11-говорит:
§7.21.5.2 функция fflush
¶2 If
stream
указывает на выходной поток или поток обновления, в котором последняя операция не была введена,fflush
функция заставляет любые неписаные данные для этого потока доставляться в среду хоста, чтобы быть записанными в файл; в противном случае поведение не определено.
POSIX в основном ссылается на стандарт C, но он отмечает этот текст как расширение C.
[CX] для a поток открыт для чтения, если файл еще не находится в EOF, и файл способен искать, смещение файла базового открытого описания файла должно быть установлено в положение файла потока, и любые символы, возвращаемые в поток
ungetc()
илиungetwc()
, которые впоследствии не были считаны из потока, должны быть отброшены (без дальнейшего изменения файла смещение).
обратите внимание, что терминалы не способны искать; ни трубы, ни розетки.
Microsoft определяет поведение fflush(stdin)
Microsoft и среда выполнения Visual Studio определяет определение поведения fflush()
на входном потоке.
если поток открыт для ввода,
fflush
очищает содержимое буфера.
Cygwin является примером довольно распространенной платформы, на которой
fflush(stdin)
не очищает входные данные.
вот почему эта версия ответа my комментарий Примечания "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- карточку (без дальнейшего изменения файла смещение.)
обратите внимание, что терминал не способен искать.