C++ Teil 8 – Arrays



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.

Eindimensionales Array

Eindimensionales Array

Zweidimensionales Array

Zweidimensionales Array

Dreidimensionales Array

Dreidimensionales Array

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};

1D Beispielarray mit Zahlen

1D Beispielarray mit Zahlen

Wenn man nicht alle Elemente Initialisiert, so werden sie mit 0 belegt.

// Beispiel
int zahlen[10] = {1,-2,4,7};

Eindimensionales Array

Eindimensionales Array

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";

Zeichenkette

Zeichenkette

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.

Initialisierung eines 2D-Arrays

Initialisierung eines 2D-Arrays

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];

Zugriff auf ein 2D-Array

Zugriff auf ein 2D-Array

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.

2D Array

2D Array

Dieses 2D-Array wird intern als folgendes eindimensionales Array dargestellt.

1D Array

1D Array

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

  1. Beschreiben Sie in eigenen Worten was Arrays sind.
  2. Wie deklariert man Arrays?
  3. Erstellen Sie ein Feld und initialisieren Sie es mit dem Satz „Ich lerne C++“. Geben Sie den Inhalt auf dem Bildschirm aus.
  4. 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.
  5. Welche Bedeutung hat der erste Zugriffsindex bei einem 2D-Array?
  6. Erstellen Sie ein beliebiges TicTacToe Feld und lasse Sie es auf dem Bildschirm ausgeben.
  7. 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.
  8. 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.
  9. Erweitern Sie da das TicTacToe-Beispiel zu einem vollwertigen Spiel.
  10. 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 =)




11 Kommentare zu “C++ Teil 8 – Arrays”

  1. abc123am 28. November 2012 um 18:07 Uhr

    bei bubblesort wieso erstmal annehmen das der array sortiert ist?
    dann beendets doch bevor es startet…

  2. Maximam 28. November 2012 um 19:21 Uhr

    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.

  3. abc123am 5. Dezember 2012 um 01:11 Uhr

    wieso macht man dann nicht bei if nicht „else sortiert = true“ dran? denn bei mir gehts irgendwie nicht :(

  4. Maximam 5. Dezember 2012 um 01:20 Uhr

    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.

  5. abc123am 5. Dezember 2012 um 13:29 Uhr

    #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;

  6. abc123am 5. Dezember 2012 um 13:36 Uhr

    bei include :
    ctime
    cstdlib
    iostream

  7. abc123am 5. Dezember 2012 um 16:53 Uhr

    auch so wie bei dir gehts nicht :(

  8. abc123am 5. Dezember 2012 um 17:03 Uhr

    ahh hab bemerkt dass nicht danach ausgegeben ^^ aber wo muss ich das dann hinschreiben? bei mir kommt dan dauernt ein Fehler

  9. abc123am 5. Dezember 2012 um 17:31 Uhr

    ohh habs doch noch geschafft kannst die anderen nachrichten ignorieren :)
    vielen dank

  10. Maximam 5. Dezember 2012 um 17:35 Uhr

    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.

  11. abc123am 19. Dezember 2012 um 17:04 Uhr

    sry hab visual c++ nicht ^^ und war eh nur = anstatt == ;)

Trackback URI | Kommentare als RSS

Einen Kommentar schreiben

XHTML: Du kannst folgende Tags verwenden: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <sub> <sup>

Hinweis: Ich behalte mir das Recht vor solche Kommentare, die Beleidigungen oder rechtswidrige Inhalte beinhalten erst nach einer Editierung freizugeben oder kommentarlos zu löschen. Ähnliches gilt auch für Kommentare die offensichtlich nur der Suchmaschinenoptimierung dienen.