Odpowiedź na to jest taka, że fflush(stream) jest tylko formalnie zdefiniowany dla strumieni wyjściowych, więc fflush(stdout) jest OK, ale fflush(stdin) nie jest.

Celem fflush(stream) jest sprawienie, by system operacyjny przepłukał wszelkie bufory do pliku bazowego. Dla przykładu uzasadnionego użycia, studenci często mają problemy typu „mój znak zachęty nie pojawia się!”, jeśli zrobią coś w stylu:

printf("Enter a number: ");

Jednakże odkrywają, że to działa po prostu dobrze:

printf("Enter a number:\n");

Oczywiście, nie chcą nowej linii po swoim znaku zachęty, więc mają pewien problem.

Powodem tego jest to, że wyjście do stdout jest buforowane przez OS i domyślnym zachowaniem jest (często) tylko faktyczne zapisanie wyjścia do terminala, gdy napotkana zostanie nowa linia. Dodanie fflush(stdout) po printf() rozwiązuje problem:

printf("Enter a number: ");fflush(stdout);

Teraz, pracując przez analogię, ludzie często myślą, że fflush(stdin) powinno odrzucać każde nieużywane wejście, ale jeśli się nad tym trochę zastanowić, to nie ma to zbyt wiele sensu. Co to znaczy „przepłukać” bufor wejściowy? Gdzie jest on „przepłukiwany”? Jeśli przepłuczesz bufor wyjściowy, wyjście jest wysyłane do pliku bazowego lub terminala, gdzie w końcu i tak by się skończyło, ale gdzie wejście „w końcu i tak by się skończyło”? Nie ma sposobu by to wiedzieć! Jakie powinno być zachowanie, jeśli dane strumienia wejściowego pochodzą z pliku, potoku lub gniazda? Nie jest wcale jasne dla strumieni wejściowych, jakie powinno być zachowanie fflush(), ale jest bardzo jasne dla strumieni wyjściowych we wszystkich przypadkach. Stąd fflush() jest zdefiniowane tylko dla strumieni wyjściowych.

Powodem, dla którego błędne użycie fflush(stdin) stało się powszechne, jest to, że wiele lat temu kilka systemów operacyjnych zaimplementowało schemat, w którym działało to tak, jak wielu ludzi oczekiwało, odrzucając nieużywane dane wejściowe. Dobrym przykładem jest Microsoft DOS. Zaskakująco, nowoczesne wersje Linuksa również implementują fflush() dla strumieni wejściowych.

Właściwą rzeczą do zrobienia z „dodatkowym” niechcianym wejściem terminala jest po prostu przeczytanie go i nic z nim nie robienie. Jest to prawie tak łatwe jak wywołanie fflush(stdin), działa wszędzie i nie polega na formalnie niezdefiniowanym zachowaniu.

Standard C mówi:

Jeśli strumień wskazuje na strumień wyjściowy lub strumień aktualizacji, w którym ostatnia operacja nie była wejściem, funkcja fflush powoduje, że wszelkie niezapisane dane dla tego strumienia, które mają być dostarczone do środowiska hosta, są zapisywane do pliku; w przeciwnym razie zachowanie jest niezdefiniowane.

POSIX mówi (również jawnie odsyła do standardu C):

Jeśli strumień wskazuje na strumień wyjściowy lub aktualizacyjny, w którym ostatnia operacja nie była wejściem, fflush() powoduje, że wszelkie niezapisane dane dla tego strumienia są zapisywane do pliku, …

Ale manpage Linuksa mówi:

Dla strumieni wyjściowych, fflush() wymusza zapis wszystkich danych buforowanych w przestrzeni użytkownika dla danego strumienia wyjściowego lub aktualizacyjnego poprzez bazową funkcję zapisu tego strumienia. Dla strumieni wejściowych, fflush() odrzuca wszelkie buforowane dane, które zostały pobrane z bazowego pliku, ale nie zostały skonsumowane przez aplikację. Nie ma to wpływu na stan otwarcia strumienia.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.