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

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