Was ist der Tastaturpuffer?

4 Antworten

Vom Fragesteller als hilfreich ausgezeichnet

Der Tastaturpuffer ist ein Dienst des Betriebssystems: Wenn ein Programm eine Tastatureingabe verlangt, wird es angehalten und der Benutzer hat beliebig viel Zeit, eine Zeile Text zu schreiben und zu editieren (einschließlich Löschen, Copy/Paste, Autocomplete, Historie, etc.). Erst wenn er „Return“ (oder Strg-D unter Unix, Strg-Z unter DOS/Windows) drückt, läuft das Programm weiter und bekommt die effektiv eingegebenen Zeichen einschließlich dem „Return“, falls vorhanden. Ob auch ein Strg-D/-Z im Puffer landet, weiß ich nicht.

scanf() liest (und entfernt) so viele Zeichen aus diesem Puffer, wie es momentan braucht. Der Rest bleibt für die nächste Eingabe stehen. Erst wenn der ganze Puffer ausgelesen wurde, stoppt das Programm wieder und der Benutzer kann eine neue Zeile Text eingeben.

Am deutlichsten wird das, wenn ein Programm zwei Zahlen liest. Du kannst schon beim ersten Prompt beide Zahlen (mit Whitespace getrennt) eingeben und dann „Return“ drücken. Das Programm merkt den Unterschied nicht – außer, es misst die Zeit zwischen der Ausgabe des Prompts und dem Speichern der Zahl. Da wundert es sich, wie schnell Du tippen kannst.

Bei der Eingabe mit scanf() stellst Du Dir am besten vor, dass die Werte aus einer Textdatei gelesen werden. Bei umgeleiteter Eingabe ist das tatsächlich so. Bei der Eingabe über die Tastatur wartet das Programm immer dann, wenn es das erste Zeichen einer neue Zeile lesen will.

Dein Programm liest tatsächlich den Zeilentrenner ein. Nur sieht der eben in der Ausgabe wie eine leere Zeile aus. Besser verständlich wird das Ganze, wenn Du nicht nur das Zeichen, sondern auch seinen ASCII-Code ausgibst:

printf("%d(%c)\n", Dummy, Dummy);

Das sollte dann etwa so aussehen:

65(A)
10(
)

Der Tastaturbuffer ist der Stream den dir das Betriebssystem zur Verfügung stellt. In diesem Buffer stehen alle Tastatureingaben drinnen die an das jeweilige Programm gesendet werden. Also jeder einzelne Tastendruck.

Da scanf nur die Zeile liest, nicht aber den Abschluss der Zeile bleibt quasi das Enter im Tastaturbuffer hängen und du musst dieses Enter erst aus dem Buffer raus holen.

Grundsätzlich geht es mit scanf("%c,&Dummy) wenn du allerdings keine Variable verwenden möchtest kannst du auch die Funktion getchar() verwenden und den Returnvalue der Funktion nicht verwenden. getchar ließt ebenfalls einen Tastendruck aus dem Tastaturbuffer aus.

Das folgende Programm liefert bei mir die gewünschte Ausgabe:

#include <stdio.h>


int main(void)
{
    int zahl = 0;
    char zeichen = 0;
    printf("Geben Sie eine Zahl ein\n");
    scanf("%d", &zahl);
    getchar();
    printf("Geben Sie ein Zeichen ein\n");
    scanf("%c", &zeichen);
    getchar();


    printf("Zahl: %d\n", zahl);
    printf("Zeichen: %c\n", zeichen);
    return 0;
}

Wenn du hier das erste getchar() weglässt wird zB die Leerzeile in die Variable zeichen geschrieben wie zu erwarten ist.

Natürlich kannst du die getchar() auch durch scanf("%c",&dummy) ersetzen und du erhältst die selbe Funktionalität, ich finds aber mit den getchar einfach schöner.

PeterKremsner  05.12.2020, 11:19

Als kleiner Tipp du kannst dir deine Dummyvariable auch so ausgeben lassen printf("%d", (int)Dummy); Dann bekommst du die Ausgabe 10.

Wenn du jetzt hier die Zahl 10 nachschlägst:

https://www.torsten-horn.de/techdocs/ascii.htm

Siehst du bei 10 den Name LF was für Linefeed steht also das was du in C mit \n schreibst. Das ist etwas besser weil dir das auch sogenannten Nonprintable Charakters ausgibt. Zwei Leerzeilen hintereinander am Programmende sind oft nur schwer von einer zu Unterscheiden, weil einige Terminals von sich aus eventuell schon Leerzeilen einfügen.

1
LuisBuzZ 
Fragesteller
 05.12.2020, 11:21

Hallo erstmal danke für die umfangreiche Antwort. Damit hast du mir sehr geholfen(Tastaturbuffer ;p..). Ich werde damit definitiv meinen code so schreiben können, dass ich ihn verstehe. Trotzdem werde ich immer noch nicht in ruhe schlafen können. Mir immer noch nicht klar, warum nun genau dieses letzte scanf bei der Eingabe von einem Zeichen und einer Zahl übersprungen wird...

0
PeterKremsner  05.12.2020, 11:32
@LuisBuzZ

Ich weiß nicht ganz was du jetzt damit meinst?

Immer wenn du eine Eingabe mit Enter bestätigst steht ein \n im Buffer das dazu führt, dass der nächste Call zu scanf dieses \n einließt.

Das letzte getchar bei mir wird Übersprungen weil im Buffer noch das \n drinnen steht.

0
LuisBuzZ 
Fragesteller
 05.12.2020, 11:58
@PeterKremsner

Ne das kann nicht sein ich habe ja ein printf mit dem letzten scanf verbunden und wenn jetzt das letzte scanf wegen dem \n übersprungen wird, dann müsste ich am ende einen Zeilenumbruch als ausgabe erhalten

0
PeterKremsner  05.12.2020, 16:15
@LuisBuzZ

Ich hab dein Programm ausprobiert und es macht genau das. Das printf nach dem scanf("%c", Dummy) generiert einen Zeilenumbruch.

0
LuisBuzZ 
Fragesteller
 05.12.2020, 11:56

Aber dieser Code hat doch das selbe Problem. Wenn ich 7A am Anfang eingebe erhalte ich kein Zeichen mehr

0
PeterKremsner  05.12.2020, 16:22
@LuisBuzZ

Ich glaube du verwechselst hier aber etwas. Damit dieser Code das tut was du willst musst du 7 Enter und dann A Enter eingeben.

7A ist ein String scanf("%d" kann mit einem String nichts anfangen womit das natürlich nicht funktioniert.

Wenn du willst, dass 7A als Zahl und ein Char erkannt wird musst du zunächst einen String auslesen dann die Zahl extrahieren und danach den Char am Schluss extrahieren. Das geht mit diesem Code:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>


int main(void)
{
    char str[10];
    printf("Eingabe: ");
    scanf("%s", str);


    int zahl = atoi(str);
    char c = str[strlen(str) - 1];


    printf("Zahl: %d\n", zahl);
    printf("Charakter: %c", c);
    return 0;
}

Hier wird zunächst der String 7A im Eingabepuffer gelesen. atoi liest nun aus dem String die Zahl aus welche zu beginn steht und wandelt diese in einen Integer um. c wird einfach nur auf das letzte Zeichen im String gesetzt. Das -1 kommt daher, dass strlen die Länge des Strings zurückgibt der Arrayindex aber 0 basiert ist womit man 1 von der Länge abziehen muss um den Index des letzten Zeichens im String zu erhalten.

Nur als Anmerkung diese verwendung von scanf sollte nur zum Testen dienen, scanf hat die Eigenschaft, dass es zu einem Bufferoverflow führt. Es gibt hier andere Funktionen zum lesen eines Strings die man in einem Produktiven Einsatz statt dem scanf("%s" verwenden sollte.

1

Wenn ich mich nicht täusche wird beim zweiten Code im mittleren scanf das \n gelesen somit ist die zweite Eingabeaufforderung eigentlich deine dritte und das Programm ist dann fertig. Also die Zahl hinterlässt bereits ein \n.

Wäre eine gute Gelegenheit den Debugger zu testen. Der wird dir noch häufiger über den Weg laufen, darin kannst du dir auch alle Attribut-Werte live anzeigen lassen. Somit siehst du was wo in welcher Zeile gespeichert ist.

LuisBuzZ 
Fragesteller
 05.12.2020, 10:48

Okay danke ich habe leider keine Ahnung was der Debugger ist aber ich gucke mal was sich online dazu finden lässt :)(habe erst vor drei Wochen angefangen zu studieren)

0
LuisBuzZ 
Fragesteller
 05.12.2020, 10:56

Achso und im Mittleren Code wird das Zeichen gelesen. wenn man dieses und das Unteren scanf löscht, dann erhält man nur eine Zahl als Rückgabe

0
rpi ~ # ./bull

geben Sie bitte eine ganze Zahl und ein Zeichen ein:32g
32g
rpi ~ #

Macht doch genau was es soll?

LuisBuzZ 
Fragesteller
 05.12.2020, 10:51

Wir haben vor drei Wochen mit c begonnen. Deine Antwort verstehe ich inhaltlich nicht und sie bietet vielleicht eine Alternative Lösung, hat aber nicht wirklich was mit dem Problem zu tun.

0
KarlRanseierIII  05.12.2020, 12:34
@LuisBuzZ

Es ist ein Ausführung Deines Quellcodes, er gibt genau das aus, was er soll, Zahl+Zeichen+Newline.

...dann müsste ich eine 7 ein A und einen Zeilenumbruch aus Ausgabe bekommen. Das Passiert aber nicht.

Doch, genau das passiert aber.

1
LuisBuzZ 
Fragesteller
 05.12.2020, 12:42
@KarlRanseierIII

Ich erhalte als Ausgabe keinen Zeilenumbruch am ende. Könnte das mit meinem Compiler zusammenhängen? Ich benutze visual studio code

0
KarlRanseierIII  05.12.2020, 13:49
@LuisBuzZ

Nein, vermutlich nicht, wenn eher, dann mit dem Terminal, das verwendet wird. Aber auch das ist eher unwahrscheinlich.

Was macht Dich so sicher, daß bei Dir kein Zeilenumbruch ausgegeben wird?

0