C++ Frage bezüglich Speicheradressierung bei Funktionen

std::random_device sollte man für sowas nicht benutzen, das ist ziemlich langsam, da nicht-deterministisch. Kommt auf das Betriebssystem an, wie das implementiert ist, aber im Allgemeinen braucht man dafür irgendein Rauschen oder Mausbewegungen oder Netzwerkverkehr oder irgendetwas anderes, das nicht deterministisch ist.

Für solche und viele andere Zwecke reichen Pseudozufallszahlen.
 
Bzgl. std::endl vs "\n" folgendes.
Bei std::cout ist es egal welches von beiden man nutzt.Unter aktuellen Linux Versionen und Windows findet auch nach "\n" ein flush statt.
Wenn man hingegen std::stringstream oder std::fstream verwendet ist "\n" schneller.
 
@Magogan: Für ein Ratespiel am PC (und viele andere Anwendungen) ist std::default_random_engine mehr als schnell genug (auf meinem Windows PC mit VS2015 spuckt es ca 20 Mio Zahlen pro Sekunde aus). Die sauberere Lösung wäre natürlich ne statische (evtl. thread-local) engine variable, die von einem random_device initialisiert wird, aber das war die simpelste Lösung die mir einfiel ohne neue Konzepte einzuführen.

@Fonce: Richtig, hatte ich nicht daran gedacht. Aber selbst dann hat "\n" noch den Vorteil, dass es kürzer ist und man sich meiner Meinung nach eben gleich angewöhnen sollte endl sparsam zu verwenden.
 
Sehr schön die Änderungen. Und toll, dass Du den Input gleich umsetzt. Da lernst Du sicher viel dabei.

differenz_spieler = differenz_spieler *(-1);
geht auch kürzer (+= kennst Du ja schon):
differenz_spieler *= -1;
(bei kürzeren Variablen Namen wäre auch i = -i; okay, oben stehendes ist Schreibfaulheit äh Effizienz)

Was mir am Ablauf jetzt noch nicht gefällt, ist die Rekursion zwischen gluecksspiel() und weiterspielen().
Die Rekursion ist mMn nicht nötig, sogar leicht verwirrend und baut auch unnötig einen Funktionscall-Stack auf. Einfach gesagt: bricht der Spieler ab, werden alle gluecksspiel() und weiterspielen() Funktionen rückwärts "zuende" geführt. Da steht bei Dir ja kein Code, kann aber zu Problemen führen. Kannst Du ausprobieren indem Du nach weiterspielen(); in gluecksspiel() noch ein std::cout << "Heute lerne ich Rekursion ;-)" << std::endl; machst.
Ich würde hier einfach eine while Schleife in gluecksspiel() einführen und weiterspielen() lediglich als Check für die Abbruchbedingung umfunktionieren.

Code:
bool weiterspielen(){
	bool weiter = false;
	
	// code zum setzen von weiter auf true, wenn j/J
	
	return weiter;
}

void gluecksspiel(){
	bool weiter = true;
	while(weiter){
		//code
	   weiter = weiterspielen();
	}
	verabschiedung();
}
 
Zuletzt bearbeitet:
Vorab, nochmal vielen Dank an alle, die sich hier beteiligen :)

@Miuwa:
Ah alles klar. Darum gibt er immer wieder die 13 aus, Danke für die Erläuterung.
Ich sollte sowas erst selber verstehen, bevor ich etwas einfach kopiere und zu nutzen versuche...

Das mit den Variablen versuche ich zukünftig zu berücksichtigen.
In einem Uni-Kurs habe ich nur gelernt, dass alle Variablen so früh wie möglich angelegt werden sollen. Am besten direkt zu Beginn, damit man später in keine Probleme läuft, weil man schon auf Variablen zugreift, die noch gar nicht existieren.

std::endl habe ich nun nach eurer kleinen Diskussion überall durch \n ersetzt.
Ich sehe aufgrund eurer Aussagen für mich auch \n praktikabler. -> Weniger zu tippen und hier keine Nachteile gegenüber std::endl.
Wobei ich auch sagen muss, dass ich noch gar nicht weis was ein "Flush" ist. Muss ich bei Gelgenheit nachschlagen.


Die Code-Vereinfachungen, besoders bei kontostand_check sind wirklich super.
Das werd ich mir auf jeden Fall merken :)


@The Ripper
Danke für den Link bzgl. der Zufallszahlen. Werde ich mir anschauen und versuchen zu verstehen!


@Del Torres
Habe ich ausprobiert und es wird so oft ausgegeben, wie man gespielt hat.
Habe mich auch noch nicht mit dem Thema Rekursion beschäftigt. Habe nur überlegt, was muss als nächstes erfolgen, wenn das passiert und es musste eben eine neue Runde vom Glücksspiel gestartet werden. Daher dachte ich mir einfach, dass ich dann die Funktion nochmal aufrufe.
Aber ich hab das jetzt mit einer while-Schleife geändert :)


Danke für eure Hilfe und eine schöne Woche.

Gruß
thesi
 
Thesi schrieb:
Wobei ich auch sagen muss, dass ich noch gar nicht weis was ein "Flush" ist. Muss ich bei Gelgenheit nachschlagen.

Ist eigentlich ganz simpel, cout schreibt alles erstmal in einen Buffer, bei einem flush wird dieser dann geleert und der Inhalt auf die Konsole geschrieben.
 
Thesi schrieb:
Habe mich auch noch nicht mit dem Thema Rekursion beschäftigt.
Code:
void recursive(int forwards){
    
    if((forwards >= 0) && (forwards < 3)){
        std::cout << "recursive(";
        recursive(forwards+1);
    }
    std::cout << ")";    
    if(forwards==0) std::cout << ";" << std::endl;
}
in Main mit recursive(0); aufrufen
 
Zuletzt bearbeitet:
Thesi schrieb:
Das mit den Variablen versuche ich zukünftig zu berücksichtigen.
In einem Uni-Kurs habe ich nur gelernt, dass alle Variablen so früh wie möglich angelegt werden sollen. Am besten direkt zu Beginn, damit man später in keine Probleme läuft, weil man schon auf Variablen zugreift, die noch gar nicht existieren.
In den alten C Standard war es noch so das man Variablen immer am Anfang deklarieren musste. Einige Leute scheinen da irgendwie stecken geblieben zu sein^^
Ich habe das auch noch vor 12 Jahren bei meiner ITA Ausbildung, als ich das erste mal mit C++ in Berührung kam(Auch wenn der Lehrer uns immer erzählen wollte das wir ja C machen :D ), so gelernt von den Lehrern. Das das aber bei den neuen Standards nicht verlang wird und sogar schlechter Stil ist musste ich mir dann selbst aneignen durch Lesen von Büchern zum Thema.
 
Thesi schrieb:
In einem Uni-Kurs habe ich nur gelernt, dass alle Variablen so früh wie möglich angelegt werden sollen.
Das war dann aber kein C++-Kurs, oder?

In C++ ist der Scope, also der Geltungsbereich einer Variablen, weitaus wichtiger als in Sprachen wie C oder Java, weil es bei denen keine Standard-Konstruktoren und Destruktoren gibt. Deswegen soltest du in C++ deine Variablen immer genau da deklarieren und initialisieren, wo du sie benötigst, nicht vorher und natürlich auch nicht nachher - und wenn du später mal mit Objekten arbeitest, die im Nachhinein nicht mehr verändert werden können, musst du das sogar.

Und auch in anderen Sprachen würde ich es nicht unbedingt empfehlen, Variablen unnötig vor der Benutzung zu deklarieren, das stört meiner Meinung nach nur die Lesbarkeit und erlaubt es, uninitialisierte Variablen zu verwenden - was kein bisschen besser ist als ein Compilerfehler bei Verwendung einer Variablen, die noch nirgendwo deklariert wurde.
 
Zuletzt bearbeitet:
VikingGe schrieb:
Das war dann aber kein C++-Kurs, oder?

Nein, der Kurs hieß "Grundlagen des Programmierens für Ingenieure", wenn ich mich noch recht erinnere.
Ist mittlerweile auch schon wieder 3-4 Jahre her...

Die Code-Beispiele aus dem Kurs waren in C geschrieben.

Aber da ging es nicht viel tiefer als Schleifen, if-Abfragen ...
 
Thesi schrieb:
Die Code-Beispiele aus dem Kurs waren in C geschrieben.
Und stammten wahrscheinlich wie der gesamte Kurs aus den 80er Jahren. :freak: Würde mich zumindest nicht wundern.

Nimm das Grundlagenwissen davon mit, bei Stilfragen gelten heute aber andere Standards, zumal du hier mit einer anderen Programmiersprache arbeitest. Auch das ist wichtig, guter Stil in C ist nämlich eher selten auch guter Stil in C++. ;)
 
VikingGe schrieb:
Und stammten wahrscheinlich wie der gesamte Kurs aus den 80er Jahren. :freak: Würde mich zumindest nicht wundern.

Wenn ich vom Alter des Dozenten ausgehe, dann könnte das tatsächlich hinkommen :D


Edit:

Ich habe gerade einen weiteren Code geschrieben und dieses Mal besonders darauf geachtet, die Variablen erst dann anzulegen, wenn ich sie wirklich brauche.
Ebenso habe ich für jede Aufgabe eine eigene Funktion geschrieben.

Würdet ihr nochmal drüber schauen, ob es so nun besser ist?

Das Programm liest eine Ganzzahl ein und gibt dann die einzelnen Ziffern der Zahl untereinander aus.


Code:
#include <iostream>

//function prototypes
int zahl_einlesen();
int anzahl_ziffern_ermitteln(int zahl);
int *ziffern_ermitteln(int zahl, int anzahl_ziffern);
void ziffern_ausgeben(int anzahl_ziffern, int *pziffern);

int main()
{
	int zahl = zahl_einlesen();

	int anzahl_ziffern = anzahl_ziffern_ermitteln(zahl);
		
	int *pziffern = ziffern_ermitteln(zahl, anzahl_ziffern);
	
	ziffern_ausgeben(anzahl_ziffern, pziffern);
	
	//Konsole bis zum naechsten Enter auf halten.
	std::cin.sync();
	std::cin.get();
	return 0;
}

int zahl_einlesen()
{
	std::cout << "Geben Sie eine Zahl ein: ";
	int eingabe;
	std::cin >> eingabe;
	return eingabe;
}

int anzahl_ziffern_ermitteln(int zahl)
{
	int anzahl_ziffern = 0;
	//Zahl durch 10 teilen, bis 0.xx rauskommen wuerde und Anzahl der durchgefuehrten Divisionen merken
	while (zahl > 0)
	{
		anzahl_ziffern += 1;
		zahl /= 10;
	}
	return anzahl_ziffern;
}

int *ziffern_ermitteln(int zahl, int anzahl_ziffern)
{
	//array zur Laufzeit für die Ziffern anlegen
	int *pziffern = new int[anzahl_ziffern];

	//Ziffern mittels Modulo Funktion ermitteln und ins array schreiben, solange bis alle Ziffern erfasst wurden
	for (int i = 0; i < anzahl_ziffern; i++)
	{ 
		pziffern[i] = zahl % 10;
		zahl /= 10;
	}

	return pziffern;
}

void ziffern_ausgeben(int anzahl_ziffern, int *pziffern)
{
	//Alle Ziffern untereinander ausgeben
	for (int i = anzahl_ziffern - 1; i >= 0; i--)
	{
		std::cout << pziffern[i] << "\n";
	}
}


Gruß
thesi
 
Zuletzt bearbeitet:
Hab nur kurz drüber geschaut, aber eines kann ich schon sagen, nimmt einfach einen std::vector
 
Von den Funktionen und Variablen her sieht das schon ganz gut aus. Der Code hat trotzdem ein Problem, und zwar reservierst du hier Speicher:

Code:
int *pziffern = new int[anzahl_ziffern];

Du gibst ihn aber nirgendwo wieder frei. Ist bei dem kleinen Programm kein Drama, aber wenn du jetzt in deiner Main-Funktion eine Schleife hättest, die ganz oft durchlaufen würde, wäre ganz schnell dein RAM voll. Daher Aufgabe 1: Finde die richtige Stelle, den Speicher per delete[] wieder freizugeben.

Außerdem schiebst du aktuell immer einen Pointer auf ein Array sowie die Länge des Arrays hin und her. Wäre irgendwie schöner, wenn man das ganze in einem Rutsch erledigen könnte, oder? Den Tip hat fonce schon gebracht - und das wäre dann Aufgabe 2 - verwende einen std::vector. Damit erledigt sich übrigens auch gleichzeitig das Problem aus Aufgabe 1, ohne, dass du dich selbst darum kümmern musst.

Aufgabe 3, vielleicht für irgendwann später:
- Schau dir an, wie Klassen funktionieren
- Bau dir eine einfache Array-Klasse, die ohne std::vector auskommt, und die Länge sowie die Daten des Arrays speichert, in etwa so wie es dein aktueller Code tut.
- Sorge dafür, dass diese auch funktioniert, wenn du ein Array kopierst (und - Bonuspunkt - verschiebst). Stichworte: Copy-Constructor, Assignment-Operator.

Warum das ganze, wenn es das schon gibt? Lerneffekt, v.a. im Bezug darauf, wie RAII in C++ funktioniert.
 
Zuletzt bearbeitet:
Also wenn du tatsächlich nur eine Zahl einlesen willst und die einzelnen Ziffern dann untereinander ausgeben willst.
Ich würde dafür z.B. einfach nur eine Funktion wie folgende schreiben.
Code:
void print_digits(int number)
{
	std::string number_as_string = std::to_string(number);
	for(auto digit : number_as_string)
	{
		std::cout << digit << std::endl;
	}
}
Wenn man es noch ein wenig kürzer möchte.
Code:
void print_digits(int number)
{
	for(auto digit : std::to_string(number))
	{
		std::cout << digit << std::endl;
	}
}

Ansonsten hier mal wie das ganze bei der Verwendung von std::vector aussehen würde. ;)

Code:
#include <iostream>
#include <vector>

int zahl_einlesen();
std::vector<int> ziffern_ermitteln(int zahl);
void ziffern_ausgeben(const std::vector<int>& digits);

int main()
{
	auto zahl = zahl_einlesen();
	auto digits = ziffern_ermitteln(zahl);
	ziffern_ausgeben(digits);
	//Konsole bis zum naechsten Enter auf halten.
	std::cin.sync();
	std::cin.get();
	return 0;
}

int zahl_einlesen()
{
	std::cout << "Geben Sie eine Zahl ein: ";
	int eingabe;
	std::cin >> eingabe;
	return eingabe;
}


std::vector<int> ziffern_ermitteln(int zahl)
{
	std::vector<int> digits;
	while (zahl > 0)
	{
		digits.push_back(zahl % 10);
		zahl /= 10;
	}
	return digits;
}

void ziffern_ausgeben(const std::vector<int>& digits)
{
	//Alle Ziffern untereinander ausgeben
	for (auto digit : digits)
	{
		std::cout << digit << std::endl;
	}
}

Achja noch was. Kommentare wie "//function prototypes" oder "//array zur Laufzeit für die Ziffern anlegen" oder "//Alle Ziffern untereinander ausgeben" sind unnötig den genau das Zeigt ja der Code. Kommentare sollten immer ausdrücken "warum man etwas macht". "Wie man etwas macht" das beschreibt ja der Code.

Dann noch ein Sache über die man natürlich geteilte Meinung sein kann. Leerzeichen im Code. Also damit meine ich jetzt innerhalb von Funktionen, Klasse usw.. Ich persönlich bin immer der Meinung das Leerzeichen Code unleserlich machen.
Aber wie gesagt, kann man geteilter Meinung zu sein. ;)
 
Oh hey Fonce, dankeschön für diese möglichen Lösungen :)

Wollte gerade meinen überarbeiteten Code posten, als ich deinen gesehen habe.

Zunächst zu "Aufgabe 1":
Die richtige Stelle für delete[] sollte dort sein, wo das letzte Mal auf das angelegte array zugegriffen wird. Insofern sollte das delete[] pziffern nach der for-Schleife in void ziffern_ausgeben stehen.


"Aufgabe 2"
Code:
#include <iostream>
#include <vector>

//function prototypes
int zahl_einlesen();
int anzahl_ziffern_ermitteln(int zahl);
std::vector<int> ziffern_ermitteln(int zahl, int anzahl_ziffern);
void ziffern_ausgeben(std::vector<int> &ziffern);

int main()
{
	int zahl = zahl_einlesen();
	int anzahl_ziffern = anzahl_ziffern_ermitteln(zahl);
	std::vector<int> ziffern = ziffern_ermitteln(zahl, anzahl_ziffern);
	ziffern_ausgeben(ziffern);

	//Konsole bis zum naechsten Enter auf halten.
	std::cin.sync();
	std::cin.get();
	return 0;
}

int zahl_einlesen()
{
	std::cout << "Geben Sie eine Zahl ein: ";
	int eingabe;
	std::cin >> eingabe;
	return eingabe;
}

int anzahl_ziffern_ermitteln(int zahl)
{
	int anzahl_ziffern = 0;
	//Zahl durch 10 teilen, bis 0.xx rauskommen wuerde und Anzahl der durchgefuehrten Divisionen merken
	while (zahl > 0)
	{
		anzahl_ziffern += 1;
		zahl /= 10;
	}
	return anzahl_ziffern;
}

std::vector<int> ziffern_ermitteln(int zahl, int anzahl_ziffern)
{
	std::vector<int> ziffern;
	
	//Ziffern mittels Modulo Funktion ermitteln und ins array schreiben, solange bis alle Ziffern erfasst wurden
	for (int i = 0; i < anzahl_ziffern; i++)
	{
		ziffern.push_back(zahl % 10);
		zahl /= 10;
	}
	return ziffern;
}

void ziffern_ausgeben(std::vector<int> &ziffern)
{
	//Alle Ziffern untereinander ausgeben	
	for (int i = ziffern.size() - 1; i >= 0; i--)
	{
		std::cout << ziffern[i] << "\n";
	}
	
}


Die Funktion anzahl_ziffern_ermiteln kann man sich natürlich sparen, wenn man darauf gekommen wäre, dass man das mit ziffern_ermitteln kombinieren kann.

Ich schieße mich irgendwie immer sofort auf for-Schleifen ein...


Das mit den Kommentaren habe ich auch schon gelesen. Aber momentan hilft es mir sehr, wenn ich einfach nochmal in einen Code reinschauen kann und dort drinsteht, was genau passiert. Wenn mehr gelernt habe und einen Code schneller verstehe, dann werde ich das auch nicht mehr brauchen. Aber so ist das aktuell noch eine gute Gedankenstütze. Und wenn ich wirklich mal nicht mehr durchblicke, was da tatsächlich abläuft, dann kann ich es mir nochmal durchlesen.


"Aufgabe 3": muss ich mich erstmal mit Klassen auseinander setzen :D Aber eventuell sollte ich das wirklich als nächstes machen, da das ja sowohl schon bei dem Glücksspiel-Code angesprochen wurde als auch bei dem hier...


Gruß
thesi
 
Perfekte Übung um Rekursion anzuwenden! Willst Du es probieren, oder soll ich Dir eine rekursive Variante posten?

Die bisherigen Lösungen geben ja das letzte Digit zuerst aus, ist das gewünscht? Mit "std::reverse(digits.begin(), digits.end());" kann man das umdrehen. Dazu brauchst Du ein include auf <algorithm>
 
Zuletzt bearbeitet:
Ich versuch erstmal selber eine rekursive Variante zu schreiben, da ist der Lerneffekt einfach immer größer :)

Die Variante von Fonce gibt zuerst die letzte Ziffer der Zahl aus. Meine Variante gibt die erste Ziffer zuerst aus.
 
Ah beim ausgeben gehst Du von der letzten Stelle los. Ich meinte aber eigentlich nicht das ausgeben, sondern konkret die Reihenfolge wie die Stellen im Array gespeichtert sind :-)
 
Zurück
Oben