C Grundlagen Konsolen-Eingaben (stdin)

RoseFlunder

Lieutenant
Registriert
Sep. 2009
Beiträge
799
Hallo,

ich fange gerade an mich mit C zu beschäftigen und in einem ersten Tutorial geht es um die Ein- und Ausgabe auf der Konsole.

Ich programmiere unter Windows 7 und benutze als Compiler MinGW und als IDE Eclipse.
Ich habe ein neues C-Projekt angelegt und MinGW als Toolchain festgelegt.

Hello World funktionierte soweit auch ganz gut.

Nun ist folgendes Beispiel im Tutorial aufgetaucht:
Code:
#include <stdio.h>

int main(void) {
	char a, b, c;

	printf("Test#1 \n");

	printf("1. Buchstabe : ");
	scanf("%c", &a);

	printf("2. Buchstabe : ");
	scanf("%c", &b);

	printf("3. Buchstabe : ");
	scanf("%c", &c);

	printf("Sie gaben ein : %c %c %c", a, b, c);
	return 0;
}

Dies funktionierte nicht wie angedacht, wurde aber auch gleich erklärt.
Da die Eingabe gepuffert wird, wird beim 2. scanf Aufruf \n zurückgeliefert.
Ich kann also im Prinzip nur den 1. und 3. Buchstaben eingeben.

Folgende Lösung wird vorgeschlagen:

Code:
#include <stdio.h>

int main(void) {
	char a, b, c;
	char buf[2];

	printf("Test#1 \n");

	printf("1. Buchstabe : ");
	fgets(buf, 2, stdin);
	sscanf(buf, "%c", &a);

	printf("2. Buchstabe : ");
	fgets(buf, 2, stdin);
	sscanf(buf, "%c", &b);

	printf("3. Buchstabe : ");
	fgets(buf, 2, stdin);
	sscanf(buf, "%c", &c);

	printf("Sie gaben ein : %c %c %c", a, b, c);
	fgets(buf, 2, stdin);
	return 0;
}

Nun verstehe ich das so, dass zwei Zeichen mit fgets eingelesen werden und ich mir daraus mit sscanf den Buchstaben holen kann, da dies auf %c passt. Das \n wird also ignoriert.
In der Theorie sollte es jetzt gehen, aber in der Praxis erlebe ich genau das gleiche Verhalten wie mit der ersten Variante :(
Woran scheitert es jetzt?
Mache ich noch etwas falsch?

LG
 
Hallo

habe es gerade unter VS2010 probiert, gleiches verhalten

der zweite parameter von fgets gibt an wieviel zeichen maximal gelesen werden (es können auch weniger sein)
d.h. er liest genau bis zum auftauchen eines newline '\n' oder bis er max-1 zeichen gelesen hat (wichtig weil er immer zum schluss ein '\0' anhängt

Das bedeutet jetzt bei dir, das er nur ein Zeichen einließt weil er keinen Platz hat das '\n' zu speichern
Einfach den Buffer erweitern (zumindest auf 3), du brauchst Platz für das Zeichen, ein '\n' und dass abschließende '\0'

Als alternative kannst du anstatt von fgets, ein normales gets nehmen. Das löscht das abchließende '\n' raus und ersetzt es durch ein '\0'
Hat aber ein sicherheitsproblem, da du nicht angeben kannst wie groß dein Buffer ist. Dadurch ist es möglich das ein "bösewicht" zu viel text eintippt und einen Speicherüberlauf produziert. Vermute aber das wird für deine Beispiele egal sein. :-)
 
Danke erst mal für die Antwort, aber so richtig habe ich es jetzt noch nicht verstanden.
Wenn ich nur die Größe des Buffers auf drei Zeichen erhöhe und die fgets aufrufe nicht anpasse, ändert sich nichts.

Wenn ich aber die Größe des Buffers auf zwei Zeichen belasse und stattdessen bei fgets den zweiten Parameter von 2 auf 3 Zeichen erhöhe, funktioniert alles.
Wieso das?
Werden jetzt drei Zeichen gelesen, also z.B. a\n\0 und nur a\n in den Buffer geschrieben?

Aber wieso funktioniert dann nicht die Variante mit Buffergröße 2 und fgets mit 2?
Demnach würde doch dann a\0 gelesen werden und dies sollte in den Buffer geschrieben werden.
Wenn ich nämlich die zweite Variante aus dem Startpost mit dem Debugger in Eclipse aufrufe, sieht es dort OK aus, kann nur nicht die Ausgabe sehen. Scheint das nicht auf die Eclipse Konsole umleiten zu wollen oder so.

Daher hier nur die Screenshots wie die Variableninhalte in Eclipse aussehen.
bild1.PNG
bild2.PNG

Wieso geht als kompilierte EXE Datei dann nur das auf Screenshot 2?
Theoretisch sieht es ja im Debugger so aus, als wenn in beiden Fällen die Variablen a und b richtig gefüllt werden.
Nur der Buffer beinhaltet im 1. Bild w\0 und im zweiten Bild w\n.

EDIT: Sorry, dass ich das noch mal hinterfrage, aber ich möchte es halt von Anfang richtig lernen und verstehen wieso die Dinge im Programm so passieren, wie sie passieren :)
 
Zuletzt bearbeitet:
Ich würde es so machen:
Code:
#include <stdio.h>

int main(void) {
        char a, b, c;

        printf("Test#1 \n");

        printf("1. Buchstabe : ");
        a = getchar();
        while(getchar() != '\n');

        printf("2. Buchstabe : ");
        b = getchar();
        while(getchar() != '\n');

        printf("3. Buchstabe : ");
        c = getchar();
        while(getchar() != '\n');

        printf("Sie gaben ein : %c %c %c\n", a, b, c);
        return 0;
}
Dadurch wird der Puffer immer geleert und es schadet auch nicht wenn der Benutzer mal mehr als ein Zeichen eingibt.
 
Vielen Dank für die alternative Lösung.
Die sieht schon sehr gut aus.

Trotzdem würde es mich aus Neugier interessieren, wieso das Programm sich so verhält wie in Post #3 beschrieben.
Es geht mir ja auch um das Verständnis und nicht nur um darum um jeden Preis etwas aus der Konsole einzulesen :)
 
Angenommen die Eingabe ist: a\n b\n c\n
(ohne die Leerzeichen)

fgets liest immer num-1 Zeichen, oder bis es ein \n gelesen hat. Im Fall num=2 wird also immer ein Zeichen gelesen.
Der erste Aufruf liest das a weg und hängt ein \0 dran. Also sollte buf = [a, \0] sein. Das \n steht aber immernoch im Puffer.
Daher wird beim zweiten Aufruf sofort aus dem Puffer das \n gelesen und buf = [\n, \0] sollte gelten.
Der Puffer ist wieder leer und das Spielchen würde sich wiederholen.

Im Fall num=3 werden 1 (\n) oder 2 (Buchstabe und \n) gelesen.
Der erste Aufruf liest also a\n und alle sind glücklich, da der Puffer wieder leer ist das sscanf() schön das a extrahiert. Es gilt buf = [a, \n] und \0 wird auf die nächste Adresse auf dem Stack geschrieben. Ich weiß jedoch nicht, ob da noch ein Byte für die Terminierung reserviert wird oder ob man hier uU ziemliche Probleme bekommen könnte.
Genauso verhält es sich mit den anderen Aufrufen.
 
OK, das klingt sinnvoll. Nur ist Eclipse dann etwas seltsam. Da scheint das \n ja nicht im Puffer zu sein, da es ja beim 2. fgets Aufruf mit num = 2 nicht auftaucht.

Edit: ah, wenn nicht die variable buf gemeint war in #2 dann macht das viel mehr Sinn.
 
Zuletzt bearbeitet:
Jetzt lies dir the_nobs' Beitrag durch und denke dabei daran, dass er mit dem Buffer nicht deinen "buf"-String meint, sondern den Puffer von fgets, der so groß ist, wie dort angegeben.

Edit: Und Beitrag #6 bezieht sich auf die scanf Variante.
 
Zurück
Oben