C++-Anfänger und Arrays: wo anfangen, zu zählen?

Angel

Angel

Aktives Mitglied
Thread Starter
Dabei seit
30.06.2003
Beiträge
7.503
Reaktionspunkte
237
Ich bin im ersten Semester Medieninformatik und muss daher C++ lernen (ja, ich hätte auch lieber mit Java angefangen).

Solange ich mich damit noch nicht so auskenne, bleibe ich lieber beim in meiner FH verwendeten Programm, sprich: Microsoft Visual Studio 2005. Also bitte verzeiht mir, dass ich (noch) keine native IDE wie Eclipse benutze! :shame:

Ich sitze gerade vor der aktuellen Aufgabe, das Thema sind Arrays. Mein Buch (C++ für Spieleprogrammierer) hilft mir dabei zwar super, aber trotzdem werde ich immer wieder vor Stolpersteine gestellt, bei denen ich nicht so recht weiter weiß. Dieses Mal ist es aber etwas anderes: ich habe an meiner Datei so lange rumgebastelt, bis alle bisher implementierten Funktionen auch funktionieren. Allerdings weiß ich nicht so genau, warum das so funktioniert. :D

Das Programm soll eine per Konstante festgelegte Anzahl an Double-Werten einlesen und in ein Array speichern. Die Werte sollen anschließend in aufsteigender Reihenfolge sortiert werden.

So sieht das ganze bei mir aus (ohne Kommentarkopf):

Code:
#include <iostream>
using namespace std;

const int Anzahl = 10;

void main()
{
	double dZahlenwerte;
	double dtemp;
	double aZahlenwerte[Anzahl];

	cout << "Bitte geben Sie die "
		 << Anzahl
		 << " Zahlenwerte ein. \n";

	for (int i=1; i<=Anzahl; i++)
	{
		cin >> dZahlenwerte;
		aZahlenwerte[i] = dZahlenwerte;
	}

	for (int a=1; a<=Anzahl; a++)
	{
		for (int i=a; i<=Anzahl; i++)
		{										
				if(aZahlenwerte[a] > aZahlenwerte[i])	
				{									
					dtemp = aZahlenwerte[a];
					aZahlenwerte[a] = aZahlenwerte[i];		
					aZahlenwerte[i] = dtemp;		
				}								
		}
	}


	cout << endl;
	cout << aZahlenwerte[1] << endl;
	cout << aZahlenwerte[2] << endl;
	cout << aZahlenwerte[3] << endl;
	cout << aZahlenwerte[4] << endl;
	cout << aZahlenwerte[5] << endl;
	cout << aZahlenwerte[6] << endl;
	cout << aZahlenwerte[7] << endl;
	cout << aZahlenwerte[8] << endl;
	cout << aZahlenwerte[9] << endl;
	cout << aZahlenwerte[10] << endl;
}

Die ganzen couts am Ende habe ich nur testweise eingebaut, um zu sehen, ob die richtigen Werte ausgegeben werden.

Was ich nicht verstehe:

In meinem Buch steht, dass der Compiler bei 0 und nicht bei 1 anfängt, zu zählen. Weil der Mensch bei 1 anfängt, würden dabei die meisten Programmierfehler passieren. Bei 10 Werten in dem Array sollte das dann doch eigentlich von 0 bis 9 gehen. Die For-Schleife fängt aber bei 1 an und geht bis 10. Ich kann auch die Variablen 1 bis 10 ausgeben. Aber wieso funktioniert das, die Variable 0 müsste doch ungenutzt und die Variable 10 gar nicht vorhanden sein?

(Das zweite Fragezeichen konnte ich gerade selbst lösen. :))

Würde mich freuen, wenn mir das jemand erklären könnte. :)
 
Du solltest immer bei 0 anfangen. Alles andere ist schlicht und einfach falsch.

Wenn du auf das Element mit Index 10 zugreifen willst greifst du einfach auf einen Speicherbereich zu, der nicht mehr zu deinem Array gehört. Das kann wie in deinem Fall gut gehen, kann aber auch ganz schnell böse enden. :D
 
ich bin jetzt zwar nur bewandert in C, aber du hast doch mit der Anzahl Variable eine INT Variable deklariert und kein Array, nen Array zu definieren würde z.B. so aussehen
double A[10] das ist ein Array der LÄNGE 10, aber angesprochen werden könenn nur die Variablen 0 bis 9
bei deinem
int Anzahl = 10 ist dies nichtzutreffend, du hast einfach da stehen Anzahl = 10 mehr nicht, daher zählt deine Schleife auf Grund des <= auch bis 10 und nich nur bis 9

hoffe das is eingermasen verständlich und richtig :/

mfg
 
Ah, was mir auch noch auffällt: Wenn dieser Code als Beispiel in dem Buch war, und du ihn nicht selbst geschrieben hast, solltest du dir ganz schnell ein anderes Buch suchen. Das ist nämlich ganz schlechter Stil! :nono:
 
Dass 0 ungenutzt ist, macht nix, da gibt es keine Regel gegen. Meine C-Kenntnisse sind etwas rostig, aber soweit ich weiss, haben die C-Arrays, die Du verwendest, kein Index-Checking sondern sind nur syntaktischer Zucker für ein bisschen Zeigerarithmetik. Deswegen klappt es, weil die Werte trotzdem in den Speicher geschrieben und aus selbigen gelesen werden und glücklicher(?) Weise an der Stelle, die illegaler Weise (mit Index 10) gelesen/beschrieben wird, keine anderen Daten liegen, die Du kaputt machst bzw. niemand, der diese Stelle legal benutzen dürfte, Dir Deine Werte überschreibt.

Konstrukte wie i=1; i<=irgenwas kannst Du übrigens in 99% der Fälle (und insbesondere im vorliegenden) durch i=0; i<irgendwas ersetzen.
 
Übrigens: am einfachsten löst du die Aufgabe mit der STL (die du ja eh verwendest) indem du die Sortierschleife ersetzt durch:

sort(aZahlenwerte,aZahlenwerte+10);

Wahrscheinlich solltest du bei der Aufgabe natürlich das selbst implementieren einer Sortierung üben, aber es schadet nichts zu wissen, dass C++ dafür schon Hausmittel mitbringt (meiner Erfahrung nach kennen sich C++-Programmierer oft nur schlecht in der STL aus).
 
Was ist da schlechter Stil :confused:

Da fallen mir direkt mehrere Sachen auf. Angefangen mit void main(). Das gibt es einfach nicht. void main() ist undefiniert! Laut Standard hat die Funktion, die beim programmstart aufgerufen wird, den Namen main und den Rückgabewert int.

Dann die Konstante Anzahl. Konstantennamen bestehen nur aus Großbuchstaben. Das ist zwar keine Pflicht, aber doch so üblich.

Und die Ungarische Notation, also die Präfixe an den Variablennamen, halte ich auch für äußerst fragwürdig, aber das ist ja zum Glück geschmackssache. :D
 
Erstmal @autoexec.bat: Danke für deine Hinweise. Aber das void main() haben wir so von unserem Prof, ich glaube das ist eine Besonderheit von Visual Studio? Jedenfalls muss das da sein, sonst gibts Punktabzug. ;)
Das mit der Konstanten werde ich gleich ändern. Da es keine Pflicht ist, halte ich es zwar nicht für einen schlechten Stil, aber dadurch sind die Konstanten besser erkennbar. Für nichts anderes ist allerdings die ungarische Notation, damit ich nicht aus Versehen mit einem falschen Datentyp arbeite. Was spricht denn deiner Meinung nach dagegen?

ich bin jetzt zwar nur bewandert in C, aber du hast doch mit der Anzahl Variable eine INT Variable deklariert und kein Array, nen Array zu definieren würde z.B. so aussehen
double A[10] das ist ein Array der LÄNGE 10, aber angesprochen werden könenn nur die Variablen 0 bis 9
bei deinem
int Anzahl = 10 ist dies nichtzutreffend, du hast einfach da stehen Anzahl = 10 mehr nicht, daher zählt deine Schleife auf Grund des <= auch bis 10 und nich nur bis 9

hoffe das is eingermasen verständlich und richtig :/

mfg
Laut meinem Buch und den Unterlagen meines Profs deklariert man Arrays entweder über array[Anzahl der Elemente]; und weist dann später jedem Element einen Wert zu (wie ich hier gemacht habe: aZahlenwerte = dZahlenwerte; ), oder man deklariert und definiert ein Array gleichzeitig: array[] = {Elemente}, was den Vorteil hat, dass der Compiler die Größe des Arrays selbst bestimmt und man es leichet erweitern kann. Allerdings sehe ich noch nicht, wie man die Werte dann vom Benutzer eingeben lassen kann.
Ich verstehe deswegen nicht, was du meinst. :confused:

Dass 0 ungenutzt ist, macht nix, da gibt es keine Regel gegen. Meine C-Kenntnisse sind etwas rostig, aber soweit ich weiss, haben die C-Arrays, die Du verwendest, kein Index-Checking sondern sind nur syntaktischer Zucker für ein bisschen Zeigerarithmetik. Deswegen klappt es, weil die Werte trotzdem in den Speicher geschrieben und aus selbigen gelesen werden und glücklicher(?) Weise an der Stelle, die illegaler Weise (mit Index 10) gelesen/beschrieben wird, keine anderen Daten liegen, die Du kaputt machst bzw. niemand, der diese Stelle legal benutzen dürfte, Dir Deine Werte überschreibt.
Wenn ich bei obigem Quelltext aZahlenwerte[0] ausgeben lasse, kommt ein undefinierter Wert. Er schreibt also definitiv nur in die Variablen 1 bis 10. Nur woher weiß er das denn? Mit Zeigern kenne ich mich noch nicht aus...

Konstrukte wie i=1; i<=irgenwas kannst Du übrigens in 99% der Fälle (und insbesondere im vorliegenden) durch i=0; i<irgendwas ersetzen.
Danke!

Wahrscheinlich solltest du bei der Aufgabe natürlich das selbst implementieren einer Sortierung üben, aber es schadet nichts zu wissen, dass C++ dafür schon Hausmittel mitbringt (meiner Erfahrung nach kennen sich C++-Programmierer oft nur schlecht in der STL aus).
Genau so ist es, aber vielen Dank für den Tipp! :)
 
Aber das void main() haben wir so von unserem Prof, ich glaube das ist eine Besonderheit von Visual Studio? Jedenfalls muss das da sein, sonst gibts Punktabzug. ;)
http://www.c-plusplus.de/forum/viewtopic-var-p-is-284489.html

Wenn ich bei obigem Quelltext aZahlenwerte[0] ausgeben lasse, kommt ein undefinierter Wert. Er schreibt also definitiv nur in die Variablen 1 bis 10. Nur woher weiß er das denn? Mit Zeigern kenne ich mich noch nicht aus...

Code:
for (int i=1; i<=Anzahl; i++)
	{
		cin >> dZahlenwerte;
		aZahlenwerte[i] = dZahlenwerte;
	}
Probier es mal mit int i=0
 
Wenn ich bei obigem Quelltext aZahlenwerte[0] ausgeben lasse, kommt ein undefinierter Wert. Er schreibt also definitiv nur in die Variablen 1 bis 10. Nur woher weiß er das denn? Mit Zeigern kenne ich mich noch nicht aus...

Naja, du deklarierst ein Array für 10 Elemente. Und diese 10 Elemente haben den Index 0 bis 9!

Das bedeutet aber nicht, daß du nicht auch ein Array-Element mit einem Index -2 oder eben 10 zugreifen kannst. Nur reservierst du eben mit der Deklaration nur Speicher für die Elemente 0 bis 9.

Dass dein Programm hier nicht böses macht oder ganz abstürzt liegt nur an dessen Einfachheit. Falsch berechnete Array-Indizes sind eine beliebte Methode Speicherbereich mit Werten zu füllen, der eigentlich für etwas anderes vorgesehen ist. :D

Nimm also eine Schleife for (i = 0; i < 10; i++)
Die fängt bei 0 an und hört mit 9 auf. Das sind genau deine 10 Elemente.

Gruß
Dirk
 
Ich glaube dir ja. Mein Prof hat auch gesagt, dass das eigentlich nicht ganz richtig sei, aber wir sollen es halt so machen, er würde uns irgendwann später mal erklären, warum. :rolleyes:
Es klang so, als läge das an Visual Studio bzw. dessen Compiler. Aber wie gesagt, ich weiß es nicht. Was der Prof will, das muss leider auch so gemacht werden. Aber ich werde es im Hinterkopf behalten!

Code:
...

	for (int i=0; i<Anzahl; i++)
	{
		cin >> dZahlenwerte;
		aZahlenwerte[i] = dZahlenwerte;
	} 

	for (int a=0; a<Anzahl; a++)		// äußere For-Schleife geht alle Variablen von 1 bis 10 durch 
	{
		for (int i=a; i<Anzahl; i++)	// innere For-Schleife vergleicht die von der äußeren Schleife angegebene
		{								// Variable mit allen darüber liegenden Variablen und guckt, ob sie kleiner sind
			if(aZahlenwerte[a] > aZahlenwerte[i])

...

Vielen Dank für eure Hilfe!

Jetzt muss ich das Tauschen der beiden Variablen in ein Unterprogramm packen, wobei ich noch keine Ahnung habe, wie ich das machen soll. :confused:
Ich werde mich mal wieder in mein Buch einlesen...
 
Hmm... schon lange her, dass ich mit c++ was gemacht habe.
aber legt man nicht einfach eine datei an, z.b. unterprogramm.h, schreibt da den käse rein und in das hauptprogramm kommt ein #include "unterprogram.h"?
 
Ob du das Unterprogramm in die gleiche Datei oder eine separate schreibst (die aber auch die Endung .cpp hat; die .h-Datei verknüpft die beiden miteinander und enthält die Definition, was man mit der Funktion alles machen kann), ist ja afaik egal. Aber alle Funktionen, die ich kenne, können nur einen Wert wieder zurück geben.
Der genaue Wortlauf der Aufgabenstellung lautet: „definieren und verwenden Sie zur besseren Verständlichkeit des Hauptprogramms für das Tauschen der Werte ein kleines Unterprogramm tausche(). In diesem Unterprogramm sollen dem Namen entsprechend nur die Werte zweier Double-Parameter getauscht werden; das Array und der Sortieralgorithmus sollen im Hauptprogramm implementiert werden.“
Das verstehe ich so, dass er beide Parameter auch wieder zurück geben soll, denn sonst bräuchte die Funktion ja nur den Wert des einen Parameters in den anderen schreiben. Ich habe schon versucht, das mit Strukturen zu lösen, aber Funktionen vom Typ double können offenbar keine Strukturen zurückgeben. :confused:
 
kann gut sein, das das darin steht, wie gesagt meine kenntnisse beziehen sich auf c und nicht c++, ich würd auf die anderen höhren ;)
 
Funktionen können in c++ nur einen "Wert" (genauer gesagt ein Objekt) zurückgeben, aber das kann auch eine Struktur sein. Du könnest tausche() also z.B. als

ZweiWerte tausche(double a, double b)

definieren, wenn du zuvor

struct ZweiWerte{...};

definierst. Das wäre allerdings arg umständlich - versuche es lieber mit einer Funktion vom Typ

void tausche(double &a, double &b)
oder
void tausche(double *a, double *b)
 
Dein Vorschlag sind Pointer, oder? Die muss ich in der zweiten Aufgabe anwenden. ;)

Danke für dein Struktur-Beispiel. So ähnlich hatte ich es, nur dass die Funktion bei mir eine double war. Wenn ich also als Typ die Struktur angebe, passt es? Gleich mal ausprobieren, Danke!
 
Mein Programm funktioniert jetzt, inkl. Unterprogramm. Allerdings ist das jetzt deutlich aufwändiger gelöst als wenn ich das Tauschen einfach ins Main implementiert hätte. Außerdem musste ich die Struktur außerhalb des Main deklarieren, damit sie auch im Unterprogramm verfügbar ist. Dadurch habe ich jetzt aber zwei lokale Variable, und das Array kann ich da nicht einbauen.

Code:
//////////////////////////////////////////////
//											//							
// Programm arraySort						//
//											//
//////////////////////////////////////////////

#include <iostream>
using namespace std;

const int ANZAHL = 10;

struct s_werte
{
	double nr1;
	double nr2;
};

s_werte tauschen(s_werte Werte);

void main()
{
	s_werte Werte;
	double dZahlenwerte;
	double aZahlenwerte[ANZAHL];

	cout << "Bitte geben Sie die "
		 << ANZAHL
		 << " Zahlenwerte ein. \n";

	for (int i=0; i<ANZAHL; i++)
	{
		cin >> dZahlenwerte;
		aZahlenwerte[i] = dZahlenwerte;
	} 

	for (int a=0; a<ANZAHL; a++)		// äußere For-Schleife geht alle Variablen von 1 bis 10 durch 
	{
		for (int i=a; i<ANZAHL; i++)	// innere For-Schleife vergleicht die von der äußeren Schleife angegebene
		{								// Variable mit allen darüber liegenden Variablen und guckt, ob sie kleiner sind
			if(aZahlenwerte[a] > aZahlenwerte[i])	
			{		
				Werte.nr1 = aZahlenwerte[a];
				Werte.nr2 = aZahlenwerte[i];
				Werte = tauschen(Werte);
				aZahlenwerte[a] = Werte.nr1;
				aZahlenwerte[i] = Werte.nr2;
			}								
		}
	}

	cout << endl
		 << aZahlenwerte[0] << endl
		 << aZahlenwerte[1] << endl
		 << aZahlenwerte[2] << endl
		 << aZahlenwerte[3] << endl
		 << aZahlenwerte[4] << endl
		 << aZahlenwerte[5] << endl
		 << aZahlenwerte[6] << endl
		 << aZahlenwerte[7] << endl
		 << aZahlenwerte[8] << endl
		 << aZahlenwerte[9] << endl;
}

s_werte tauschen (s_werte Werte)
//////////////////////////////////////////////////////////////////////
//																	//
// Die Funktion tauschen zwei Werte einer Struktur miteinander aus	//
//																	//
//////////////////////////////////////////////////////////////////////
{
	double dtemp = Werte.nr1;
	Werte.nr1 = Werte.nr2;
	Werte.nr2 = dtemp;
	return Werte;				// Zurückgegeben wird die Struktur Werte bestehend aus den Variablen nr1 und nr2. 
}

Die Variablen aZahlenwerte[a] und aZahlenwerte sind doch nur in den jeweiligen For-Schleifen verfügbar, deswegen kann ich sie nicht im Unterprogramm verwenden oder in der Struktur definieren.

Kann mir jemand sagen, ob es trotzdem irgendeine Möglichkeit gibt, das jetzt zu vereinfachen (vor allem die Stelle mit der Übergabe an das Unterprogramm), ohne das Array dynamisch zu allokieren?
 
...das jetzt zu vereinfachen (vor allem die Stelle mit der Übergabe an das Unterprogramm), ohne das Array dynamisch zu allokieren?
Mir gefällt weniger, dass Du haufenweise Kopiervorgänge in Kauf nimmst, um die Werte gegeneinander auszutauschen: Zuerst auf die Struktur, dann die Struktur auf Parameterposition, dann der Austausch, Rückgabe und Zuordnung zu den Array-Elementen.
Ich würde wohl, wenn schon mit Funktionsaufruf, eine Sortierfunktion mit Referenzparametern oder mit Zeigerparametern auf die Arrayelemente anlegen. Innerhalb der Funktion würden die Elemente nur bei Bedarf gegeneinander ausgetauscht.
Aber auch für eine unbedingte Tauschfunktion sparst Du die Kopiererei und die s_werte-Struktur.

Edit: Black_Smurf hat es beschrieben ...
 
also n paar Dinge sind schon richtig kritisiert worden, aber n paar (Variablendeklaration,...)dann doch eher nicht.
also erstmal möcht ich kurz Stellung zu dem void main () beziehen...
nach langem hin und her mit den ganzen compilern ist es mittlerweile legitim void anstelle von int zu benutzen. Verstehen kann ich euch schon.
Void hat immerhin 0 bit, sprich gibt keinen Wert zurück. Ist aber jetzt in jedem Compiler mit drin, bei int main (...) müsstest ja noch am ende n return 0; o.ä. schreiben, um zu zeigen, dass das Programm fertig ist.
Ich programmier auch mit Visual c++ 2005 express. Aber alles was ich programmieren muss, schreib ich auch bzw vorher in xcode!!
und das kann ich auch nur jedem raten!!! Xcode ist viel übersichtlicher, gerade mit vielen Schleifen... irgendwann weist ja nicht mehr wo du einich bist. Bei Xcode kannst dir die Schleife optisch hervorheben, mit nem simplen herüberfahren der mouse...

und jetzt noch zu deinem quelltext:
probierst du einich n boublesort bzw musst du das mit nem boublesort-verfahren schreiben?
wenn ja melde dich mal bei mir per pn, dann kann ich dir das ganze erklären und dir n beispiel schicken (selbst geschrieben)

(me=student 1. semester *g*)
 
Zurück
Oben Unten