Endlosschleife trotz richtige Abfrage in While schleife

B

ByeBye 177919

Hey!

Ich habe grad ein kleines Problem im folgendem Code:

Code:
#define _USE_MATH_DEFINES
#include <cmath>
#include <iostream>
#include <string>

using namespace std;

int main ( ) {

	char z='y';

	while ( z == 'y' ) {	


	cout << "Aufgabenblatt 1-5 Anwendungsentwicklung" << endl << endl;

	cout << "Willkommen! Das ist eine kleine Sammlung an Aufgaben, die ich in AWE bearbeitet habe, einfach eine Funktion auswaehlen, und die passende Zahl eingeben" << endl;

	cout << endl << endl << "1) Hallo Welt" << endl << "2) Hallo Welt mit Nameneingabe" << endl << "3) Zwei Widerstaende in einer Reihenschaltung addieren" << endl << "4) Zwei Widerstaende in einer Parallelschaltung addieren" << endl << "5) Kreisberechnung" << endl << "6) Kugelberechnung" << endl;

	cout << endl << "Programm waehlen: ";

	double a;

	cin >> a;

	cout << endl;

	while ( a != 1 && a != 2 && a != 3 && a != 4 && a != 5 && a != 6 ) {

		cout << "UNGUELTIGE EINGABE, BITTE NEU EINGEBEN: ";
		cin >> a;
		
	}

	if ( a == 1 ) {

		cout << endl << "Hallo Welt!";

		cin.get();

	} else if ( a == 2 ) {
		 
	   string name;
 
	   cout << "Name: ";   
	   cin >> name;
 
       cout << endl << "Hallo " << name << "!" << endl;

	   cin.get();

	} else if ( a == 3 ) {

	   double cheese, burger;

	   cout << "Erste Zahl: ";
	   cin >> cheese; 
	
	   cout << "Zweite Zahl: ";
	   cin >> burger;

       double fin = cheese+burger;
		
	   cout << "Ergebniss: ";
       cout << fin;

	   cin.get( );


	} else if ( a == 4 ) {

	   double cheese, burger;

	   cout << "Erste Zahl: ";
	   cin >> cheese; 
	
	   cout << "Zweite Zahl: ";
	   cin >> burger;

	   double fin = (cheese*burger)/(cheese+burger);
		
	   cout << "Ergebniss: ";
	   cout << fin;

       cin.get( );
	
	} else if ( a == 5 ) {

	   double r;

	   cout << "Programm zur Kreisberechnung. Flaecheninhalt, Umfang, Durchmesser" << endl << endl << endl;

	   cout << "Radius eingeben: ";
	
	   cin >> r;

	   cout << endl << "Flaecheninhalt: " << M_PI*r*r;
	   cout << endl << "Umfang: " << 2*M_PI*r;
	   cout << endl << "Durchmesser: "<< 2*r;

	   cin.get( );

	} else if ( a == 6 ) {

	   double r;

	   cout << "Programm zur Kugelberechnung. Oberflaeche, Volumen" << endl << endl << endl;

	   cout << "Radius eingeben: ";
	
	   cin >> r;

	   cout << endl << "Oberflaeche: " << 4*M_PI*r*r;
	   cout << endl << "Volumen: " << 4*M_PI*r*r*r/3;
	   
	   cin.get( );



	}


	cin.get( );

	system("cls");
	cout << endl << "Weiter machen? (Y/n)";
	cin >> z;
	
	system("cls");

	


	}


}

Und zwar bei der Zeile 29-34. Also:

Code:
  while ( a != 1 && a != 2 && a != 3 && a != 4 && a != 5 && a != 6 ) {
 
        cout << "UNGUELTIGE EINGABE, BITTE NEU EINGEBEN: ";
        cin >> a;
        
    }

Und zwar funktioniert das, wenn ich z.B 39424 eingebe, dann fragt er nach einer korrekten Angabe. Gebe ich jedoch ein Buchstaben oder andere Zeichen ein, läuft das auf eine Endlosschleife hinaus.

Lehrer kann mir leider nicht helfen xD

Weißt einer wieso?

Habe schon alles mögliche versucht: Variable als char deklarieren und dann die Abfrage in der If in ' klammern zu setzen. Die While schleife zwangsweiße zu pausieren mit system("pause") etc pp.

Probiert es einfach mal aus, komme einfach nicht weiter :confused:
 

deepthroat

Erfahrenes Mitglied
Hi.

a ist ein double.

1. Problem: vergleiche mit == oder != und double kann zu Überraschungen führen, da double Werte inhärent ungenau sind. Meist ist ein Vergleich abs(a - b) < epsilon (wobei epsiolon ein relativ kleiner Wert ist) besser

2. Problem: wenn du etwas falsches eingibst, wird nichts eingelesen und der Stream (cin) wird in einen Fehlerzustand versetzt. Von einem solchen Stream kann dann nichts mehr eingelesen werden.

Deshalb sollte man immer prüfen, ob überhaupt etwas eingelesen wurde bevor man mit dem eingelesenen Wert weiterarbeitet. Ggf. muss dann der Stream zurückgesetzt und Daten ignoriert werden.
C++:
do {
  if (cin >> a
      && (a == 1 || a == 2 || a == 3 || a == 4 || a == 5 || a == 6 )) {
     break;
  } else if (cin.error()) {
     cerr << "I/O error";
     break;
  } else if (cin.eof()) {
     break;
  } else if (cin.fail()) {
        cin.clear(); // Fehlerzustand zurücksetzen
        cin.ignore(100, '\n'); // Zeile ignorieren
  }

   cout << "UNGUELTIGE EINGABE, BITTE NEU EINGEBEN: ";
} while (cin && cout);
Warum verwendest du nicht einen unsigned int wenn nur Werte von 1 bis 6 erlaubt sind? Dann kannst du einfach auf 1 <= a <= 6 prüfen.
 
Zuletzt bearbeitet:
B

ByeBye 177919

Hallo, dein Code funktioniert leider nicht. Es kommt immer beim Compilieren:

Code:
1>------ Erstellen gestartet: Projekt: aufgabenkollektiv, Konfiguration: Debug Win32 ------
1>  aufgabenkollektiv.cpp
1>c:\users\tomekk\c++\aufgabenkollektiv\aufgabenkollektiv\aufgabenkollektiv.cpp(33): error C2039: 'error': Ist kein Element von 'std::basic_istream<_Elem,_Traits>'
1>          with
1>          [
1>              _Elem=char,
1>              _Traits=std::char_traits<char>
1>          ]
========== Erstellen: 0 erfolgreich, 1 fehlerhaft, 0 aktuell, 0 übersprungen ==========

Ich deute daraus das vllt ein Header fehlt?!

2. Problem: wenn du etwas falsches eingibst, wird nichts eingelesen und der Stream (cin) wird in einen Fehlerzustand versetzt. Von einem solchen Stream kann dann nichts mehr eingelesen werden.

Verstehe ich das in der Art richtig?: Also wenn ich eine Variable als double, oder int z.b deklariere, ist "cin" so schlau und weißt das und schreibt die falscheingabe erst garnicht in die Variable? Wieso wenn ja?

Warum verwendest du nicht einen unsigned int wenn nur Werte von 1 bis 6 erlaubt sind? Dann kannst du einfach auf 1 <= a <= 6 prüfen.

Ich bin grade im zweitem Lehrjahr :) Und das Programm was ich schrieb, ist schon eigentlich mehr als ich bzw wir machen sollten. Die einzelnen "Punkte" wie "hallo welt", "hallo welt mit namen" etc waren eigentlich einzelne Programme. Da ich jedoch vor inzwischen fast 7 Jahre hier schon mal aktiv war, und viel mit PHP/Mysql/bash etc zutun hatte war ich damit recht gut vertraut. Somit fällt mir jetzt das C++ lernen recht einfach, und ich kann wenigstens bis jetzt das was ich geschrieben habe, in nem "richtigen" C++ schreiben, und nicht C++/C mischmasch wie bei meiner Schnupperphase vor 7 Jahren :rolleyes:

Außerdem konnte ich mir damit dicke extra Punkte beim Lehrer abholen und somit sehr gut punkten *g*

Beim Thema unsigned, long, short, etc sind wir noch gar nicht. Werde es mir jetzt am Wochenende aber direkt aneignen/lesen!

Ist unsigned int dafür besser geeignet? Nur wegen Prinzip ala Speicherverbrauch/Sicherheitsgründen bzw Eleganter ist oder hat das wirklich ein ernsten Hintergrund? Bin inzwischen schon von Aufgabe 1 - 10 :)

Edit: Nur so nebenbei: Wie kann ich hier im Forum explizit C++ Code taggen? So wie bei dir.
 
Zuletzt bearbeitet von einem Moderator:

sheel

I love Asm
Beim Thema unsigned, long, short, etc sind wir noch gar nicht.
In Kürze:

char, short/short int, int, long (int), long long (int) usw.
sind Variablen für ganzzahlige Zahlen (also ohne Kommastellen)
Der Unterschied ist, wie große Werte sie speichern können (und wieviel Byte sie brauchen)
short, 2 Byte, geht zB. von -32768 bis +32767
(nicht bis +32768 weil 0 auch Platz braucht,
aber +0 und -0 nicht getrennt gespeichert werden müssen.
Dafür eben eine negative Zahl mehr)
Ein 4-bytiges long geht von -2147483648 bis +2147483647
usw.

signed/unsigned:
Von den genannten Variablen gibt es je zwei Varianten. Am Beispiel short:
Ein signed short geht von -32768 bis +32767
Ein unsigned short geht von 0 bis +65535
Mit unsigned kann man also keine negativen Zahlen speichern,
hat dafür aber nach oben hin doppelt so viel Platz.

float/double sind für Zahlen mit Kommastellen
Unterschied wieder in Byteverbrauch und Wertebereich/Kommagenauigkeit.
double ist besser.

Ist unsigned int dafür besser geeignet? Nur wegen Prinzip ala Speicherverbrauch/Sicherheitsgründen bzw Eleganter ist oder hat das wirklich ein ernsten Hintergrund?
Ja, ist besser geeignet.
Computer haben bei Kommazahlen generell das Problem, dass alles nur näherungsweise geht.
Wirklich alles, auch die Rechnungen, die eigentlich schön auf ganze Zahlen ausgehen.

(Nicht gutes) Beispiel:
(3/7)-(3/7)
Als Mensch kann man sofort sagen, dass da 0 herauskommt.
Am Computer wirds aber eventuell etwas wie 0.000000000028 oder -0.000000000057
Also knapp 0, aber eben nicht genau.
Ein einfaches "if(variable == 0.0)" geht dann daneben.
Man müsste prüfen, ob der Wert in einem bestimmten (kleinen) Bereich
in der Nähe vom Ergebnis liegt.

Damit wirds
a) Umständlicher zu Programmieren
b) Langsamer beim Ausführen (wenn man Milliarden solcher Rechnungen macht
macht das einen Unterschied)
c) Man hat ein höheres Risiko, die Spezialbehandlung einmal zu vergessen
und damit einen Programmierfehler zu haben.

Wenn man keine Kommazahlen braucht...int ist einfacher.

Edit: Nur so nebenbei: Wie kann ich hier im Forum explizit C++ Code taggen? So wie bei dir.
[code=cpp]...[/code] statt [code]...[/code]
 

deepthroat

Erfahrenes Mitglied
Hallo, dein Code funktioniert leider nicht. Es kommt immer beim Compilieren:

Code:
1>------ Erstellen gestartet: Projekt: aufgabenkollektiv, Konfiguration: Debug Win32 ------
1>  aufgabenkollektiv.cpp
1>c:\users\tomekk\c++\aufgabenkollektiv\aufgabenkollektiv\aufgabenkollektiv.cpp(33): error C2039: 'error': Ist kein Element von 'std::basic_istream<_Elem,_Traits>'
1>          with
1>          [
1>              _Elem=char,
1>              _Traits=std::char_traits<char>
1>          ]
========== Erstellen: 0 erfolgreich, 1 fehlerhaft, 0 aktuell, 0 übersprungen ==========

Ich deute daraus das vllt ein Header fehlt?!
Nein, das war ein Fehler meinerseits.
C++:
/* statt */ // } else if (cin.error()) {
} else if (cin.bad()) {
Verstehe ich das in der Art richtig?: Also wenn ich eine Variable als double, oder int z.b deklariere, ist "cin" so schlau und weißt das und schreibt die falscheingabe erst garnicht in die Variable?
Korrekt.
Was sollte denn in die Variable geschrieben werden? Und wozu?
Beim Thema unsigned, long, short, etc sind wir noch gar nicht. Werde es mir jetzt am Wochenende aber direkt aneignen/lesen!
Du hättest auch einfach int wählen können.
Ist unsigned int dafür besser geeignet? Nur wegen Prinzip ala Speicherverbrauch/Sicherheitsgründen bzw Eleganter ist oder hat das wirklich ein ernsten Hintergrund?
Die Wahl des richtigen Datentyps ist ziemlich essentiell um sich nicht mit Problemen (Ungenauigkeit) und Grenzfällen (neg. Zahlen, Überlauf) herumschlagen zu müssen, die man sich bei falscher Wahl einhandelt.
 
B

ByeBye 177919

Wunderbar, funktioniert wie es soll :) Danke.

deepthroat hat gesagt.:
Was sollte denn in die Variable geschrieben werden? Und wozu?

Na ich kenne das nur von PHP, da ist es mehr oder weniger ja egal :)

Aber gut zu wissen! Wusste nicht das die einzelnen "Funktionen" ala cin, cout, usw so "intelligent" sind und gegenseitig auf sich "aufpassen".


deepthroat hat gesagt.:
Die Wahl des richtigen Datentyps ist ziemlich essentiell um sich nicht mit Problemen (Ungenauigkeit) und Grenzfällen (neg. Zahlen, Überlauf) herumschlagen zu müssen, die man sich bei falscher Wahl einhandelt.

Alles klar! Werde ich berücksichtigen und alles jetzt anpassen.

So, da habe ich jetzt den Code von dir, der mir jetzt natürlich ein bisschen neu bzw fremd vorkommt.

C++:
do {
  if (cin >> a
      && (a == 1 || a == 2 || a == 3 || a == 4 || a == 5 || a == 6 )) {
     break;
 } else if (cin.bad()) {
     cerr << "I/O error";
     break;
  } else if (cin.eof()) {
     break;
  } else if (cin.fail()) {
        cin.clear(); // Fehlerzustand zurücksetzen
        cin.ignore(100, '\n'); // Zeile ignorieren
  }
 
   cout << "UNGUELTIGE EINGABE, BITTE NEU EINGEBEN: ";
} while (cin && cout);

Z.b wieso das do { } bzw was ist das genau für eine Funktion, und wieso wird dann am ende while da reingezogen. Also diese Art Syntax ist mir völlig fremd :)


Und das man in einer If auch funktionen aufrufen kann, wusste ich noch garnicht, bzw kenn ich nur von PHP. Wusste nicht das C++ das auch kann. Daher versteh ich den größten teil.

Nur nicht z.B wann er I/O Error ausspuckt. Habe alles mögliche versucht das hervorzurufen :) cin.eof ist vermute ich mal end of file.

Könntest es mir ein bisschen den Schnippsel? :)

Vielen Dank, hat mir sehr geholfen!
 

sheel

I love Asm
Hi

Das do-while:
Ist einfach eine Art von Schleife (gibt es in PHP übrigens auch)
For-Schleifen wie "for(i = 0; i < 10; i++) {...}" kennst du wahrscheinlich
und While wie zB. "while(i < 10) {...}" auch.
Bei der While-Schleife wird am Anfang als Erstes die Bedingung geprüft,
dann ggf. der Code drin ausgeführt, dann wieder geprüft, ausgeführt...usw.

Das
C++:
do {
    ...
} while(bedingung);
ist im Prinzip auch eine while-Schleife, mit dem Unterschied,
dass die Bedingung erst nach dem Codeausführen geprüft wird
(deswegen steht das while auch unten)
Der Code wird also sicher mindestens einmal gemacht,
und dann ggf. je nach Bedingung wiederholt.

Das do am Anfang kennzeichnet nur den Anfang des betroffenen Blocks,
hat sonst keine besondere Bedeutung.
Sonst könnte man den {...}-Block auch einfach ohne Wiederholung
lösgelöst vom while danach sehen
(und das while hat dann keinen Codeblock, weil mit ; abgeschlossen.
Syntaktisch möglich.)


Zu den Funktionen in Bedingungen (geht auch in PHP):
Der Returnwert ist dann eben ausschlaggebend.
Selber Effekt wäre
C++:
int i = funktionsaufruf();
if(i)
...

Wenn die Varialbe nur so, ohne Vergleich zu irgendwas, da steht,
bedeutet der Wert 0 falsch (also nicht ausführen), alles andere ist true.

Zu bad:
Wenn eben irgendein Fehler aufgetreten ist
(gröbere Fehler als falsche Eingaben).

edit: 6000 :D
 
B

ByeBye 177919

Ah ok.

Also ist eine Schleife mit do { } while () also eleganter und sicherer als eine pure while schleife z.B da man in vornherein auf Errors etc prüft?

Gut zu Wissen :)

Danke.
 

sheel

I love Asm
?
Es ist wirklich nur eine normale Schleife,
aber während while min. 0 mal, max. unendlich ausgeführt wird,
ists es bei do-while min. 1 mal (und max. unendlich).
Das könnte man auch machen mit while machen, in dem man den Schleifeninhalt
vor der Schleife einfach noch einmal hinschreibt.
C++:
bla1;
bla2;
bla3;
while(bedingung) {
    bla1;
    bla2;
    bla3;
}
ist das Selbe wie
C++:
do {
    bla1;
    bla2;
    bla3;
} while(bedingung);

Vom Prinzip her ist es hier sowas:

Mit do-while:
Eingeben.
Wenn Falscheingabe: Wieder eingeben
Wenn Falscheingabe: Wieder eingeben
Wenn Falscheingabe: Wieder eingeben
...

Einfach auf while umändern wäre
Wenn Falscheingabe: Wieder eingeben
Wenn Falscheingabe: Wieder eingeben
Wenn Falscheingabe: Wieder eingeben

Es würde also schon auf Falsches geprüft werden, wenn noch gar nichts eingegeben wurde.
Dewegen: Zuerst einmal eingeben lassen, dann erst mit dem Prüfen anfangen.

Entweder macht man den Einlesecode noch einmal vor der while-Schleife,
oder man verwendet einfach do-while.