Einleitung
Ein Array (zu Deutsch: ein Feld) ist eine zusammenhängende Folge von Elementen eines bestimmen Datentyps.
Arrays können ein oder mehrdimensional sein. Ein eindimensionales Feld entspricht einer einspaltigen Tabelle von Elementen(Variablen), zweidimensionales einem Schachbrett, dreidimensionales einem „Block aus Würfeln“, Felder mit mehr als drei Dimensionen sind bildlich kaum vorstellbar.
Deklaration & Initialisierung
Statische Felder werden ähnlich einer einfacher Variable deklariert, wobei am Ende des Arraynamens eckige Klammern dazu kommen. In der Klammer steht die Anzahl der Elemente, die ein Array haben soll.
// Deklaration Datentyp Arrayname[Anzahl der Arrayelemente]; // int-Array für 10 Zahlen int zahlenreihe[10];
Die Anzahl der Elemente muss logischerweise eine Ganzzahl sein. Zusätzlich muss es sich um eine Konstante handeln, damit der Compiler bereits beim Kompilieren weiß, wie groß das Array sein muss. Diese Beschränkung bezieht sich nur auf statische Arrays, um die es hier geht.
Wie auch bei einer Variablendeklaration ist auch bei den Arrays sehr wichtig diese auch zu initialisieren. Dies geschieht entweder gleichzeitig mit der Deklaration oder man initialisiert später jedes Element einzeln.
Schauen wir uns zuerst die direkte Initialisierung an.
(Datentyp) Arrayname[Anzahl der Elemente(N)] = {Wert1, Wert2, Wert3, …, WertN};
Beispiel:
// eine Tabelle mit Zahlen int primzahlen[10] = {2, 3, 5, 7, 11, 13, 17, 19, -5, 4};
Wenn man nicht alle Elemente Initialisiert, so werden sie mit 0 belegt.
// Beispiel int zahlen[10] = {1,-2,4,7};
Da C++ eine typsichere Programmiersprache ist, müssen die zugewiesenen Werte den gleichen Datentyp, wie das Array haben. Daraus folgt, dass ein Array nicht Elemente mit verschiedenen Datentypen beinhalten kann. So würde beispielsweise folgender Code einen Compilerfehler verursachen:
// erzeugt einen Fehler float kommazahlen[5] = {3.14, "hallo", 1, 'a', 5};
Initialisiert man alle Elemente per Hand, so kann man die Arraylänge bei der Deklaration weglassen, sie entspricht dann der Anzahl der Elemente bei der Initialisierung. Das ist besonders praktisch, wenn man ein Array für Buchstaben erstellt. Somit muss man die einzelnen Buchstaben nicht zählen.
// Eine Zeichenkette char zeichenkette[] = "Das ist ein Text";
Wie man erkennt, sieht die Initialisierung von char-Arrays etwas handlicher aus, so dass man nicht jeden einzelnen Buchstaben mit Komma getrennt angeben muss, was aber auch geht.
char zeichenkette[] = {'H', 'a', 'u', 's', '\0'}; // entspricht char zeichenkette[] = "Haus";
Damit haben wir auch gleich kennengelernt, wie in C++ Texte in Variablen gespeichert werden, nämlich in char-Arrays. Man beachte, dass bei einer manuellen Initialisierung eines Zeichenarrays am Ende ein ‚\0‘ draufgehängt werden muss. Daran erkennen die Funktionen, wie zum Beispiel std::cout, dass die Zeichenkette an dieser Stelle zu Ende ist.
Führt man zum Beispiel folgenden Code aus, so wird die Zeichenkette nur bis zum ‚\0‘-Escape-Sequenz ausgegeben.
// gibt nur den Text bis '\0' aus char zeichenkette[] = "Das ist \0ein Text"; std::cout << zeichenkette;
Für das Arbeiten mit Strings gibt es eine bessere Lösung als char-Arrays, nämlich die Klasse std::string, die man für den produktiven Einsatz den Arrays immer vorziehen sollte. Trotzdem stellen die Arrays die absolute Grundlage für die Verwendung von Zeichenketten dar, so arbeitet auch die string-Klasse intern mit Arrays.
Zugriff
Der Lese- und Schreibzugriff auf ein Element in einem Array erfolgt über die eckige Klammer, wobei darin die Elementnummer angegeben wird.
Beispiel:
// Array erstellen int array[5] = {1,3,5,7,9}; // viertes Element ausgeben std::cout << "4. Element" << array[3] << std::endl;
Nein, das ist kein Fehler, das vierte Element in dem Array wird mit dem Index 3 angesprochen, weil die Indexzählung in C++, wie bei fast allen modernen Programmiersprachen, bei 0 beginnt. Man zählt also nicht 1,2,3,4,5 , sondern 0,1,2,3,4. Dies muss man immer im Hinterkopf behalten, ansonsten sind Laufzeitfehler vorprogrammiert.
Möchte man auf ein Arrayelement zugreifen, dass gar nicht vorhanden ist, dann führt es meistens (aber nicht immer, was gerade das tückische daran ist) zu einem Laufzeitfehler und somit zu einem Programmabsturz. Dies ist in Verbindung mit Zeigern wohl eine der häufigsten Ursachen für Programmabstürze überhaupt.
Dazu ein paar Beispiele zum selbst ausprobieren.
// führt zur Laufzeitfehlern (nicht zwangsläufig) int zarray[5] = {1,3,5,7,9}; zarray[5]; // Zugriff auf das sechste Element => Fehler zarray[-1]; // Zählung beginnt bei 0 => Fehler
Schauen wir uns noch ein Beispiel zum Schreibzugriff an.
int zarray[5] = {1,3,5,7,9}; // viertes Element ausgeben std::cout << "4. Element" << zarray[3] << std::endl; // Wert an der 4ten Position ändern zarray[3] = 3121; // viertes Element ausgeben std::cout << "4. Element" << zarray[3] << std::endl;
Wir haben uns angeschaut wie man eindimensionale Felder erstellt, initialisiert, die Werte darin ausliest und verändert. Als nächstes betrachten wir höherdimensionale Felder.
Mehrdimensionale Felder
Die Deklaration bei mehrdimensionalen Feldern ist nicht weiter kompliziert und geht analog zu eindimensionalen Arrays. Pro eine zusätzliche Dimension hängt man eine [Anzahl der Elemente]-Klammer bei der Initialisierung dran.
Datentyp arrayname[Anzahl der Elemente(N)][Anzahl der Elemente (M)]… [Anzahl der …] ;
Beispiel für ein 2D Feld:
char array2d[2][3] = {{'a', 'b', 'c'}, {'x', 'y', 'z'}};
Ein zweidimensionales Array ist praktisch eindimensionales Array, welches als Datentyp ein eindimensionales Array hat.
Wenn man das erst mal verinnerlicht hat, dann wird die oben verwende Syntax für die Initialisierung sofort schlüssig. Man Initialisiert ein eindimensionales Array, das aus zwei weiteren eindimensionalen Arrays besteht. Diese Betrachtung kann weiter auf Felder höherer Dimensionen übernommen werden.
Aufpassen sollte man beim Zugriff auf die Elemente des Arrays. Der erste Index spiegelt bei den zweidimensionalen Arrays die Y-Richtung und der zweite die X-Richtung. Will man beispielsweise in dem oberen Array das ‚z‘ durch ‚f‘ ersetzen, so greift man auf das Feld mit array2d[1][2] zu und nicht array2d[2][1], denn dieses Element existiert nicht. Am besten wir schauen uns ein weiteres Beispiel an.
// Array erstellen (drei Zeilen mit je 4 "Zellen") int array2d[3][4] = {{1, 3, 5, 7}, {2, 4, 6, 8}, {9,11,13,15}}; // die 5 holen. int zahl = array2d[0][2]; // die 2 holen zahl = array2d[1][0]; // die 15 holen zahl = array2d[2][3];
Als nächstes noch ein Beispiel für ein 3D-Array.
Nun wird in einer Arrayzelle nicht nur ein Wert gespeichert, sondern ein Array in einer Größe für 6 char-Werte.
// Array erstellen // drei zeilen mit je 2 "zellen". jede zelle kann 6 char-werte beinhalten char array3d[3][2][6] = {{"Das", "ist"}, {"ein", "3D"}, {"Array", "."}}; // das Wort '3D' ausgeben std::cout << array3d[1][1] << std::endl; // den Buchstaben 3 aus 3D augeben std::cout << array3d[1][1][1] << std::endl;
Damit sollten die Grundlagen der statischen Felder klar sein. Als nächstes betrachten wir einige zusätzliche Informationen.
Mehrdimensionale Arrays auf eindimensionale reduzieren
Intern werden die mehrdimensionalen Felder als eindimensionale verwaltet. Die Darstellung mit den mehrfachen Klammern in mehreren Dimensionen ist nur dafür da, um dem Programmierer die Handhabung zu erleichtern.
Schauen uns wieder das obere 2D-Feld Beispiel mit Zahlen an.
Dieses 2D-Array wird intern als folgendes eindimensionales Array dargestellt.
Die einzelnen Zeilen liegen im Speicher einfach nacheinander. Die Länge dieses Arrays entspricht logischerweise dem Produkt aus der Höhe und Breite des 2D-Arrays.
// Array erstellen. entsprich int array2d[hoehe][breite] const int hoehe = 3, breite = 4; int array2d[hoehe*breite] = {1, 3, 5, 7, 2, 4, 6, 8, 9, 11, 13, 15};
Der Zugriff auf die Elemente ist auch nicht weiter schwierig. Bezeichnen wir den ersten Index mit y und den zweiten als x, so können wir mit arrayname[y*breite+x] auf jedes Element in dem eindimensionalen Array zugreifen, als ob es ein zweidimensionales wäre.
// Array erstellen. entsprich int array2d[hoehe][breite] const int hoehe = 3, breite = 4; int array2d[hoehe*breite] = {1, 3, 5, 7, 2, 4, 6, 8, 9, 11, 13, 15}; // die 5 holen. entspricht array2d[0][2] int zahl = array2d[0*breite+2]; // die 2 holen. entspricht array2d[1][0] zahl = array2d[1*breite+0]; // die 15 holen. entspricht array2d[2][3] zahl = array2d[2*breite+3];
Man überzeuge sich selbst durch Nachzählen und durch das Ausführen des Beispiels (was man sowieso immer tun sollte), dass die Methode funktioniert.
Diese Methode wird vor allem im Zusammenhang mit Bilddateien bzw. Texturen benutzt, weil die Daten dabei meistens in Form eines eindimensionalen Arrays vorliegen.
Belegter Speicher
Manchmal ist es interessant zu wissen, wie viel Speicherplatz ein Array belegt. Dies kann man mit der C++-Internen Funktionen int sizeof() bewerkstelligen.
Beispiel:
// Array erstellen und Text ausgeben wchar_t eintext[] = L"Dieses Beispiel zaehlt die Anzahl der Zeichen in diesem Satz."; std::wcout << eintext << std::endl; // die Größe des Arrays in Bytes bestimmen int satzgroesse = sizeof(eintext); std::cout << "Satzgroesse in Bytes:" << satzgroesse << std::endl; // die Größe des ersten Elements bestimmen (sie ist für alle Elemente gleich) int elementgroesse = sizeof(eintext[0]); std::cout << "Groesse eines Arrayelements in Bytes:" << elementgroesse << std::endl; // die Länge berechnen std::cout << "Zeichen Anzahl: " << (satzgroesse / elementgroesse) << std::endl;
Das ist eine, wenn auch umständliche, Art die Anzahl der Arrayelemente zu bestimmen. Dieses Beispiel sollte nur die Anwendung von sizeof() verdeutlichen, weil dies vor allem später im Zusammenhang mit dynamischer Speicherverwaltung recht wichtig sein kann.
Etwas Anwendung
Im Grunde haben wir bereits alles erfahren, um mit den statischen Arrays zu arbeiten. Im Folgenden werden wir noch einige Beispiele durchgehen.
Alle Elemente eines Arrays ausgeben
Alle Elemente durchlaufen und mit cout ausgeben.
// array erstellen const int laenge = 10; int zahlen[laenge] = {10,2,-4,4,1,7,3,7,9,-2}; // alle elemente des arrays durchlaufen for(int i = 0; i < laenge; i++) { // auf das Element mit dem Index i zugreifen und ausgeben std::cout << "Element Nr. "<< i << ": " << zahlen[i] << std::endl; }
Einen String nach einem bestimmten Buchstaben durchsuchen.
Eine lineare Suche in einem Array: Man geht alle Elemente der Reihe nach durch und schaut ob es eine Übereinstimmung mit dem gesuchten Element gibt.
// zeichenkette anlegen char satz[] = "Hier gibt es was Wichtiges zu erfahren."; // nach diesen buchstaben wird gesucht char gesucht = 'z'; // das ganze array durchlaufen for(int i = 0; i < sizeof(satz)/sizeof(char); i++) { // stimmt das arrayelement mit dem gesuchten zeichen überein? if(satz[i] == gesucht) { std::cout << "Zeichen "<<gesucht << " an der Position "<< (i+1) << " gefunden." << std::endl; } }
Zwei Werte im Array vertauschen.
Dieses Beispiel zeigt, wie man mit Hilfe einer Variablen zwei Werte im Array vertauschen kann.
// array anlegen int zahlen[10] = {10,2,-4,4,1,7,3,7,9,-2}; // wert 1 in einer variable zwischenspeichern int temp = zahlen[4]; // die 9 auf die position der der eins schreiben zahlen[4] = zahlen[8]; // die eins auf die position der ursprünglichen 9 schreiben zahlen[8] = temp;
Bubblesort
Aufbauend auf dem letzten Beispiel werden wir einen einfachen Sortieralgorithmus implementieren – den Bubblesort-Algorithmus.
Die Idee besteht darin alle Elemente des unsortierten Arrays zu durchlaufen und zu schauen ob jeweils zwei benachbarte Werte in der richtigen Reihenfolge stehen. Wenn sie nicht richtig stehen, dann werden sie, wie oben gezeigt, vertauscht. Natürlich genügt ein Durchgang nicht, weil nicht alle Zahlen in einem Durchgang auf die richtige Position gebracht werden können.
Eine Beispielimpementierung:
// ein int-array mit ungeordneten zahlen const int laenge = 8; int intarray[8] = {9,3,2,4,5,8,0,4}; // diese variable gibt an, ob das Feld sortiert ist bool sortiert = false; // so lange das Feld nicht sortiert ist, wiederhole den Sortiervorgang while(sortiert == false) { // erst mal annehmen, dass das Array sortiert ist sortiert = true; // alle (bis auf das letzte) Elemente eines Arrays durchlaufen for(int i = 0; i < laenge-1; i++) { // benachbarte Elemente stehen in falschen Reihenfolge.. if(intarray[i] > intarray[i+1]) { // ...das heißt das Array ist nicht sortiert => // man sollte es nach diesem Durchgang noch mal durchlaufen sortiert = false; // beide Elemente vertauschen int temp = intarray[i]; intarray[i] = intarray[i+1]; intarray[i+1] = temp; } } }
Dies waren ein paar Beispiele zu Arrays, die ihre Handhabung verdeutlichen sollten. Aber alle Beispiele der Welt bringen nichts, wenn man nicht selbst übt, etwas Code eintippt und etwas Eigenes ausprobiert. Aus diesem Grund sollte man auf jeden Fall die bereitgestellten Übungsaufgaben lösen.
Abschließend möchte ich noch deutlich machen, dass wir hier nur mit statischen Arrays gearbeitet haben. Dynamische Arrays, die zur Laufzeit ihre Größe ändern können und die wir später kennen lernen werden, setzen das Wissen von Zeigern voraus. Das Konzept der Zeiger werde ich im nächsten Abschnitt vorstellen.
Viel Spaß!
Übungsaufgaben
- Beschreiben Sie in eigenen Worten was Arrays sind.
- Wie deklariert man Arrays?
- Erstellen Sie ein Feld und initialisieren Sie es mit dem Satz „Ich lerne C++“. Geben Sie den Inhalt auf dem Bildschirm aus.
- Wie kann man sich mehrdimensionale Arrays vorstellen? Wie werden sie deklariert und initialisiert? Erstellen Sie min. 3 Beispielfelder mit unterschiedlichen Datentypen und initialisieren Sie sie.
- Welche Bedeutung hat der erste Zugriffsindex bei einem 2D-Array?
- Erstellen Sie ein beliebiges TicTacToe Feld und lasse Sie es auf dem Bildschirm ausgeben.
- Fordern Sie den Benutzer auf X- und Y-Koordinate(1,2,3) und das gewünschte Symbol(X oder O) für das TicTacToe-Feld einzugeben. Daraufhin sollte das Feld mit dem neu gesetzten Symbol wieder ausgegeben werden. Tipp: mit system(„cls“); auf Windows- bzw. system(„clear“); auf Linux-Systemen kann der Inhalt der Konsole geleert werden.
- Erweitern Sie das TicTacToe Beispiel so, dass abwechselnd zwei Spielen ihre Symbole eingeben können. Nach jeder Eingabe, soll überprüft werden, ob einer der beiden Spieler gewonnen hat oder ob es unentschieden steht. Sinnvollerweise verpackt man die Spiellogik in einer Schleife. Empfehlung: Überlegungen macht man sich auf einem Blatt Papier.
- Erweitern Sie da das TicTacToe-Beispiel zu einem vollwertigen Spiel.
- Erweitern Sie das Bubblesort-Beispiel mit einer Textausgabe nach jedem Schritt und überlegen Sie sich warum es Bubble(Luftblase)sort heißt. Tipp: den Kopf nach links neigen kann hilfreich sein =)
bei bubblesort wieso erstmal annehmen das der array sortiert ist?
dann beendets doch bevor es startet…
Man muss irgendwie sicherstellen, dass die Schleife so lange wiederholt wird bis es bei einem Durchgang keine Vertauschungen gibt. Der obere Code macht genau das. Überlege es dir mit einem Beispiel oder laufe der Code mit einem visuellen Debugger (in Visual C++) Schritt für Schritt durch, dann siehst du es auch.
wieso macht man dann nicht bei if nicht „else sortiert = true“ dran? denn bei mir gehts irgendwie nicht :(
Das würde nicht funktionieren, weil du die Variable „sortieren“ in einem Schleifendurchgang setzen würdest. z.B. hast du in einem Schritt eine Vertauschung, dann sagst du „aha, die Liste ist nicht sortiert“, dann setze ich sortieren=false. Das nächste Zahlenpaar braucht dann aber keine Vertauschung und du setzt sortieren=true. Was aber falsch wäre.
Man kann die Steuerung auch mit „else sortiert = true“ umschreiben, aber dann muss es auch im if-block und die Initialisierung davor ändern.
Was genau klappt denn nicht? Poste deinen Code.
#include
#include
#include
using namespace std;
int main(int argc, char *argv[])
{
int zahlarray[7];
srand(time(0));
for (int i = 0; i < 7; i++)
{
zahlarray[i] = rand() % 50 +1;
cout << zahlarray[i] << " ";
}
const int derwert = 7;
bool richtig = false;
while (richtig == false)
{
for (int i = 0; i zahlarray[i+1])
{
richtig = false;
int temp = zahlarray[i];
zahlarray[i] = zahlarray[i+1];
zahlarray[i+1] = temp;
}
else
{
richtig = true;
}
}
}
return 0;
bei include :
ctime
cstdlib
iostream
auch so wie bei dir gehts nicht :(
ahh hab bemerkt dass nicht danach ausgegeben ^^ aber wo muss ich das dann hinschreiben? bei mir kommt dan dauernt ein Fehler
ohh habs doch noch geschafft kannst die anderen nachrichten ignorieren :)
vielen dank
Tja, deswegen sage ich: benutze einen Debugger und führe deinen Algorithmus Schritt für Schritt aus. In Visual C++ ist ein guter Debugger drin, man muss ihn nur nutzen.
sry hab visual c++ nicht ^^ und war eh nur = anstatt == ;)