Forum: PC-Programmierung C, Verständnisfrage zu Lehrbuch


von C Anfänger (Gast)


Lesenswert?

Hallo erstmal,

ich bin grade ganz am Anfang des lernens von C mittels folgenden Online 
Lehrbuch:
http://openbook.rheinwerk-verlag.de/c_von_a_bis_z/index.htm#_top

Ich versuche das Buch durchzuarbeiten und dabei die Beispiele so weit 
wie möglich auch zu verstehen. Jetzt bin ich auf eine Stelle gestoßen, 
die ich leider nicht verstehe, daher hoffe ich das mir dies jemand 
erklären kann. Es geht wohl um die Pufferung von Eingaben, ist Kapitel 
4.1.2 des Buchs. Die Beispiele sind alle so vom Autor übernommen.

Folgendes Beispiel soll den Fehler zeigen, wenn man die Pufferung nicht 
berücksichtigt:
1
/* scanf2.c */
2
#include <stdio.h>
3
4
int main(void) {
5
   char a,b,c;
6
   printf("1. Buchstabe : ");
7
   scanf("%c",&a);
8
   printf("2. Buchstabe : ");
9
   scanf("%c",&b);
10
   printf("3. Buchstabe : ");
11
   scanf("%c",&c);
12
   printf("Sie gaben ein : %c %c %c ",a,b,c);
13
   return 0;
14
}

Ich denke verstanden zu haben, dass hier durch den noch gespeicherten 
Wert aus dem stdin die zweite Zeile zur Eingabe übersprungen wird. Jetzt 
bietet der Autor folgende Lösungsansätze dafür:

Möglichkeit 1:
1
/* scanf3.c */
2
#include <stdio.h>
3
4
int main(void) {
5
   char a,b,c;
6
   printf("1. Buchstabe : ");
7
   scanf("%c",&a);
8
   fflush(stdin);
9
   printf("2. Buchstabe : ");
10
   scanf("%c",&b);
11
   fflush(stdin);
12
   printf("3. Buchstabe : ");
13
   scanf("%c",&c);
14
   printf("Sie gaben ein : %c %c %c ",a,b,c);
15
   return 0;
16
}

Möglichkeit 2:
1
/* scanf4.c */
2
#include <stdio.h>
3
4
int main(void) {
5
   char a, b, c;
6
   printf("1. Buchstabe : ");
7
   do {scanf("%c",&a);} while ( getchar() != '\n' );
8
   printf("2. Buchstabe : ");
9
   do {scanf("%c",&b);} while ( getchar() != '\n' );
10
   printf("3. Buchstabe : ");
11
   do {scanf("%c",&c);} while ( getchar() != '\n' );
12
   printf("%c %c %c\n", a, b, c);
13
   return 0;
14
}

Möglichkeit 3:
1
/* scanf5.c */
2
#include <stdio.h>
3
4
int main(void) {
5
   char ch;
6
   char buf[2];
7
8
   printf("Ein Zeichen bitte : ");
9
   fgets(buf, 2, stdin);
10
   sscanf(buf, "%c", &ch);
11
   printf("Das Zeichen : %c\n",ch);
12
   return 0;
13
}

Die erste und zweite Möglichkeit funktionieren so und ich denke ich 
verstehe auch warum. Im ersten wird der stdin nach der Eingabe gelöscht, 
in der zweiten Möglichkeit wird die Abfrage nur gestartet, wenn kein 
Newline mehr im Speicher ist.

Nun habe ich versucht die 3. Möglichkeit um eine weitere 
Eingabeaufforderung zu verändern:
1
/* scanf2.c */
2
#include <stdio.h>
3
4
int main(void) {
5
   char ch;
6
   char buf[2];
7
   char ch2;
8
   printf("Ein Zeichen bitte : ");
9
   fgets(buf, 2, stdin);
10
   sscanf(buf, "%c", &ch);
11
  // fflush(stdin);
12
   printf("Zweites Zeichen bitte : ");
13
   fgets(buf, 2, stdin);
14
   sscanf(buf, "%c", &ch2);
15
   printf("Das Zeichen : %c %c \n", ch, ch2);
16
17
   return 0;
18
}

Dabei passiert wieder der gleiche Effekt, wie im Ausgangsbeispiel des 
Autos, dass die zweite Eingabe nicht abgewartet wird, bzw der 
Pufferspeicher noch belegt ist. Erst wenn ich wieder den Speicher 
flushe, funktioniert das ganze wie gewollt. Nun erschließt sich mir 
jedoch nicht, warum die Erweiterung nicht funktioniert, ohne den 
Speicher zu flushen.

von C Anfänger (Gast)


Lesenswert?

Leider zu früh auf den Antwortbutton gekommen, daher sry für den 
Doppelpost. Ich wollte eigentlich noch fragen, mit welchem Datentyp ich 
hierbei arbeiten müsste, bzw wie der Code angepasst werden muss, wenn 
ich Beispielsweise ein Datum eingeben lassen will. Erstmal nur eine 
Zeile z.B. als Input 13, die nächste 5 und die letzte 1995. Wenn ich das 
mit dem aktuellen Code mache, dann gibt er immer nur das erste 
eingegebene Zeichen von jeder Abfrage zurück.

von Nase (Gast)


Lesenswert?

C Anfänger schrieb:
> Möglichkeit 1:
Kann man so machen.
Man könnte auch das Terminal umstellen, sodass es nicht mehr puffert. 
Aber klar, du hast das Problem ja erkannt und das kannst du später immer 
noch tun.

C Anfänger schrieb:
> Möglichkeit 2:
Das ist Frickel.
Wenn der Benutzer auf die erste Frage zwei Zeichen eingibt, muss er auch 
zweimal Return drücken, denn in jedem Schleifendurchlauf wird das scanf 
ja auch wieder aufgerufen.
Vermutlich sollte es eher so aussehen(*):
1
scanf("%c", &a); while (getchar() != '\n');

C Anfänger schrieb:
> Möglichkeit 3:
An und für sich ein sinnvoller Weg. Und sogar fgets benutzt (und nicht 
das verbotene gets). Aber es ist halt falsch implementiert. Les dir mal 
eine Beschreibung von fgets durch.
Vermutlich sollte es so aussehen (Puffer länger!):
1
   char ch;
2
   char buf[20];
3
4
   printf("Ein Zeichen bitte : ");
5
   fgets(buf, 20, stdin);
6
   sscanf(buf, "%c", &ch);

Mit buf[2] hast du dasselbe Problem wie eingangs: fgets wird nur exakt 
ein einzelnes Zeichen lesen. Der Puffer hat zwar Platz für zwei Zeichen, 
aber fgets braucht noch einen Platz für ein abschließendes 0-Zeichen.

von Dirk B. (dirkb2)


Lesenswert?

1. Die Online-Version ist noch die 1. Auflage mit allen Fehlern.
2. Der Autor des Buches ist in anderen Foren nicht gerade beliebt.

3. fflush auf Eingabeströme (z.B. stdin) ist vom Standard nicht gedeckt.
Da kann alles Mögliche passieren. Windows macht da was. Linux nicht.

Alle Lösungen haben unterschiedliches Verhalten.
Das eigentliche Problem lässt sich einfach lösen: mit einem Leerzeichen 
im Formatstring von scanf vor dem %c
1
   printf("1. Buchstabe : ");
2
   scanf(" %c",&a);
3
//        ^ da
(Bei den anderen beiden scanf auch.)

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

C Anfänger schrieb:
> Erstmal nur eine
> Zeile z.B. als Input 13, die nächste 5 und die letzte 1995.
1
int tag, monat, jahr;
2
scanf("%d%d%d", &tag, &monat, &jahr);
 Du kannst da nach jeder Zahl ein Enter machen oder alles in einer Zeile 
mit Leerzeichen getrennt eingeben.

scanf benutzt bei die meisten Formatspecifiern sog. Whitespace als 
Trennzeichen.
Aber gerade bei %c nicht, da du ja (möglicherweise) genau diese Zeichen 
einlesen möchtest.
Das Leerzeichen im Formatstring sagt scanf "überlese alle Whitespace"

scanf lässt auch Zeichen, die nicht mehr zum Formatspecifier passen, 
im Eingabestrom stehen, so dass sie bei nächsten mal eingelesen werden.

Das macht Probleme, wenn du Ziffern lesen willst, aber Buchstaben 
eingibst.
Der Buchstabe verschwindet nicht aus dem Eingabestrom.
Dagegen hilft
1
int c; // int c, nicht char c.
2
while ((c = getchar()) != EOF && c != '\n');
Überlesen bis zum nächsten '\n' (Quelle: 
https://www.c-plusplus.net/forum/39349 )

Das fflush(stdin) löscht alle bisher noch nicht eingelesenen Zeichen.
Ist aber nicht portabel.

fgets betrachtet das '\n' als Endekennung und liest es auch mit ein.

von C Anfänger (Gast)


Lesenswert?

Danke erstmal für die Hilfe, ich bastel jetzt grade ein wenig damit 
herum.

Wenn das eine alte Onlineversion ist und die scheinbar eh nicht den 
besten Ruf hat, gibt es denn gute Alternativen? Ebook (muss nicht 
zwingend kostenlos sein) oder als Online Lehrbuch würde mir sehr helfen.

von Dirk B. (dirkb2)


Lesenswert?

Da kommen die Fragen
- deutsch und/oder englisch
- kannst du schon Programmieren (eine andere Sprache)


Es gibt keine guten, freien Online-Tutorials für C auf deutsch.

Wie es mit englisch und/oder kostenpflichtig aussieht, kann ich dir 
nicht sagen.

Sicher gibt es mittlerweile auch die gedruckten Bücher als EBook

Eine englische Liste gibt es bei 
http://stackoverflow.com/questions/562303/the-definitive-c-book-guide-and-list

von C Anfänger (Gast)


Lesenswert?

Deutsch oder Englisch ist mir eigentlich egal. Eine andere 
Programmiersprache kann ich bisher leider nicht.

Hab mich mal durch deine Liste bissel durchgewühlt, dass folgende fand 
ich recht ansprechend:
http://www.amazon.de/Programming-Success-Day-3rd-programming-ebook/dp/B00Q3Y55PM/ref=sr_1_1?s=books-intl-de&ie=UTF8&qid=1433924251&sr=1-1&keywords=C+Programming+Success

Und 2,99€ klingt auch sehr fair wie ich finde.

von C Anfänger (Gast)


Lesenswert?

Grade nochmal bissel gesucht, ich versuchs jetzt erstmal mit:
http://publications.gbdirect.co.uk/c_book/

Danke erstmal für die Hilfe, ich bin gespannt wie ich vorran komme :)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.