A válasz erre az, hogy a fflush(stream)
formálisan csak kimeneti folyamra van definiálva, tehát a fflush(stdout)
rendben van, de a fflush(stdin)
nem.
A fflush(stream)
célja, hogy az operációs rendszer az esetleges puffereket a mögöttes fájlba flush-olja. Egy példa a jogos használatra: a diákoknak gyakran akadnak olyan problémáik, mint a “nem jelenik meg a promptom!”, ha valami olyasmit csinálnak, mint:
printf("Enter a number: ");
Mégis úgy találják, hogy ez tökéletesen működik:
printf("Enter a number:\n");
Nem akarnak persze új sort a promptjuk után, ezért van egy kis problémájuk.
Az ok az, hogy a stdout
kimenetet az operációs rendszer puffereli, és az alapértelmezett viselkedés (gyakran) csak akkor írja ki ténylegesen a kimenetet a terminálra, ha újsorral találkozik. Egy fflush(stdout)
hozzáadása a printf()
után megoldja a problémát:
printf("Enter a number: ");fflush(stdout);
Most, analógiával dolgozva, az emberek gyakran gondolják, hogy a fflush(stdin)
-nek el kellene dobnia minden fel nem használt bemenetet, de ha egy kicsit belegondolunk, ennek nincs sok értelme. Mit jelent egy bemeneti puffert “lehúzni”? Hová “öblítik”? Ha egy kimeneti puffert lehúzunk, a kimenet a mögöttes fájlba vagy a terminálba kerül, ahová végül úgyis kerülne, de hová kerülne a bemenet “végül úgyis”? Ezt nem lehet tudni! Mi legyen a viselkedés, ha a bemeneti adatfolyam egy fájlból, csőből vagy foglalatból érkezik? A bemeneti adatfolyamok esetében egyáltalán nem egyértelmű, hogy mi legyen a fflush()
viselkedése, de a kimeneti adatfolyamok esetében minden esetben nagyon is egyértelmű. Ezért a fflush()
csak kimeneti adatfolyamokra van definiálva.
A fflush(stdin)
hibás használata azért vált általánossá, mert sok évvel ezelőtt néhány operációs rendszer valóban megvalósított egy olyan rendszert, ahol ez úgy működött, ahogy sokan elvárták, a fel nem használt bemenet eldobása. A Microsoft DOS jó példa erre. Meglepő módon a Linux modern változatai is implementálják a fflush()
-t a bemeneti folyamra.
A helyes dolog, amit a “felesleges”, nem kívánt terminálbemenettel tenni, egyszerűen az, hogy elolvassuk, és nem csinálunk vele semmit. Ez majdnem olyan egyszerű, mint a fflush(stdin)
hívása, mindenhol működik, és nem támaszkodik formálisan meghatározatlan viselkedésre.
A C szabvány szerint:
Ha a folyam olyan kimeneti folyamra vagy frissítési folyamra mutat, amelyben a legutóbbi művelet nem input volt, az fflush függvény hatására az adott folyamhoz tartozó minden meg nem írt adat a gazdakörnyezetbe kerül, hogy a fájlba íródjon; egyébként a viselkedés nem meghatározott.
A POSIX azt mondja (explicit módon is eltér a C szabványtól):
Ha a folyam olyan kimeneti folyamra vagy frissítési folyamra mutat, amelyben a legutóbbi művelet nem volt bemenet, az fflush() az adott folyamra vonatkozó minden meg nem írt adatot a fájlba írásra késztet, …
De a Linux manpage szerint:
Kimeneti folyam esetén az fflush() kikényszeríti az adott kimeneti vagy frissítési folyam összes felhasználói térben pufferelt adatának írását a folyam mögöttes írási függvényén keresztül. Bemeneti adatfolyamok esetében az fflush() minden olyan pufferelt adatot elvet, amely a mögöttes fájlból lett lekérve, de az alkalmazás még nem fogyasztotta el. A folyam nyitott állapotát ez nem befolyásolja.