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


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

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

4 58

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 очищает содержимое буфера.

M. MПримечания:

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- карточку (без дальнейшего изменения файла смещение.)

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