## 2.5 Datenfelder (Arrays) Arrays, die auch als Datenfelder bezeichnet werden, ermöglichen es, eine nahezu beliebig große Anzahl von Variablen gleichen Namens und gleichen Datentyps zu definieren. Unterschieden werden die einzelnen Elemente nur anhand einer Indizierung. Arrays kommen insbesondere dann zum Einsatz, wenn in Programmschleifen dieselben Operationen auf alle oder einen Teil der Elemente ausgeführt werden sollen. ### 2.5.1 Deklaration und Initialisierung eines Arrays Die Deklaration eines Arrays wird am besten an einem Beispiel verdeutlicht:
    int[] myArr;
Mit dieser Anweisung wird das Array myArr deklariert, das Integerzahlen beschreibt. Um wie viele es sich handelt, ist noch nicht festgelegt. Die Kennzeichnung als Array erfolgt durch die eckigen Klammern, die hinter dem Datentyp angegeben werden müssen. Danach folgt der Bezeichner des Arrays. Das Array myArr ist zwar deklariert, aber noch nicht initialisiert. Insbesondere benötigt die Laufzeitumgebung eine Angabe darüber, wie viele Elemente sich im Array befinden. Arrays werden von der .NET-Laufzeitumgebung als Objekt angesehen, deshalb unterscheidet sich die Initialisierung von der einer herkömmlichen Variablen:
    int[] myArr;
    myArr = new int[3];
Das Schlüsselwort new kennzeichnet die Erzeugung eines Objekts. Dahinter wird der Datentyp genannt. Die Anzahl der Array-Elemente – man spricht auch von der Größe des Arrays – geht aus der Zahlenangabe in den eckigen Klammern hervor: In unserem Fall verwaltet das Array myArr genau drei Integerzahlen. Die Angabe in den eckigen Klammern der Initialisierung ist immer eine Zahl vom Typ int.
Hinweis
Die Anzahl der Elemente eines Arrays ergibt sich aus der Angabe in den eckigen Klammern bei der Initialisierung mit new.
Eine alternativ gleichwertige Deklarations- und Initialisierungsanweisung ist einzeilig und bietet sich insbesondere dann an, wenn bei der Deklaration bekannt ist, wie viele Elemente das Array haben soll:
    int[] myArr = new int[3];
Alle Elemente dieses Arrays sind danach mit dem Wert 0 vorinitialisiert. Steht zum Deklarationszeitpunkt bereits fest, welche Daten die Array-Elemente aufnehmen sollen, bietet sich auch die literale Initialisierung an, bei der die Daten in geschweiften Klammern bekannt gegeben werden:
    int[] myArr = new int[3]{23,9,7};
Gleichwertig ist auch diese Initialisierung:
    int[] myArr = new int[]{23,9,7};
Wer es ganz besonders kurz mag, darf auch die folgende Schreibweise einsetzen, bei der die Größe des Arrays automatisch anhand der Anzahl der zugewiesenen Elemente bestimmt wird:
    int[] myArr = {23,9,7};
Die literale Initialisierung setzt voraus, dass allen Elementen ein gültiger Wert übergeben wird. Deshalb ist die folgende Initialisierung falsch:
    // fehlerhafte literale Initialisierung
    int[] myArr = new int[3]{23};
### 2.5.2 Zugriff auf Array-Elemente Bei der Initialisierung eines Arrays werden die einzelnen Elemente durchnummeriert. Dabei hat das erste Element den Index 0, das letzte Element den Index
    Anzahl der Elemente - 1
Ein Array, das mit
    long[] lngArr = new long[3];
deklariert und initialisiert worden ist, enthält somit drei Elemente:
    lngArr[0]
    lngArr[1]
    lngArr[2]
Wollen Sie auf ein bestimmtes Element zugreifen, beispielsweise um den Inhalt auszuwerten oder einen neuen Inhalt zuzuweisen, geschieht das ebenfalls über den Index, der hinter dem Array-Namen in eckigen Klammern angegeben wird. Betrachten wir zur Verdeutlichung wieder das mit
    int[] myArr = new int[3];
deklarierte Array, das drei Elemente enthält. Beabsichtigen wir, dem ersten Element des Arrays die Zahl 55 zuzuweisen, müsste die Anweisung wie folgt lauten:
    myArr[0] = 55;
Analog erfolgt auch die Auswertung des Elementinhalts durch die Angabe des Index:
    int iVar = myArr[0];
Im folgenden Beispiel werden zwei Arrays deklariert und mit Werten initialisiert, die anschließend an der Konsole ausgegeben werden.
    // --------------------------------------------------------------
    // Beispiel: ...\Kapitel 2\ArrayDemo
    // --------------------------------------------------------------
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    namespace ArrayDemo {
      class Program {
        static void Main(string[] args) {
          long[] lngVar = new long[4];
          string[] strArr = new String[2];
          //Wertzuweisungen
          lngVar[0] = 230;
          lngVar[1] = 4711;
          lngVar[3] = 77;
          strArr[0] = "C# ";
          strArr[1] = "macht Spaß!";
          //Konsolenausgabe
          Console.WriteLine("lngVar[0] = {0}",lngVar[0]);
          Console.WriteLine("lngVar[1] = {0}",lngVar[1]);
          Console.WriteLine("lngVar[2] = {0}",lngVar[2]);
          Console.WriteLine("lngVar[3] = {0}",lngVar[3]);
          Console.Write(strArr[0]);
          Console.WriteLine(strArr[1]);
          Console.ReadLine();
        }
      }
    }
Das Array lngVar hat eine Größe von insgesamt vier Elementen und ist vom Typ long; das Array strArr vom Typ string enthält zwei Elemente. Bis auf das dritte Element des long-Arrays mit dem Index 2 wird allen Elementen ein Wert zugewiesen. Die Ausgabe des Programms zur Laufzeit lautet:
    lngVar[0] = 230
    lngVar[1] = 4711
    lngVar[2] = 0
    lngVar[3] = 77
    C# macht Spaß!
### 2.5.3 Speicherabbild eines Arrays Ein Array beschreibt eine bestimmte Anzahl typgleicher Elemente, die im Speicher aufeinanderfolgen. Angenommen, es sei das Array arr vom Typ int deklariert (der Typ spielt bei unserer theoretisch ausgerichteten Betrachtung keine Rolle) und den einzelnen Feldelementen werden die folgenden Werte zugewiesen:
    int[] arr = new int[7];
    arr[0] = 72;
    arr[1] = 23;
    arr[2] = 24;
    arr[3] = 88;
    arr[4] = 10;
    arr[5] = 10;
    arr[6] = 45;
Nehmen wir nun rein hypothetisch an, das erste Element (arr[0]) würde die Speicheradresse 4000 für sich reservieren. Dann könnte sich das Abbild des von uns betrachteten Speicherbereichs so präsentieren wie in Abbildung 2.9. Hier klicken, um das Bild zu vergrößern Abbildung 2.9 Speicherabbild eines Arrays Mit der Zuweisung eines Werts an ein Element des Arrays bzw. mit dem Auswerten des Inhalts erfolgt ein direkter Zugriff auf die Adresse des entsprechenden Elements. Interessant wird es, wenn Sie das Array einer zweiten Array-Variablen zuweisen wollen. Darunter ist nicht die Schaffung einer Kopie (Clone) zu verstehen, sondern der Zugriff auf die Originaldaten des ursprünglichen Arrays – nur unter einem anderen Bezeichner. Betrachten Sie dazu das folgende Beispiel ArrayAdresse:
    // --------------------------------------------------------------
    // Beispiel: ...\Kapitel 2\ArrayAdresse
    // --------------------------------------------------------------
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    namespace ArrayAdresse {
      class Program {
        static void Main(string[] args) {
          int[] arr = new int[7];
          arr[0] = 72; arr[1] = 23; arr[2] = 24; arr[3] = 88;
          arr[4] = 10; arr[5] = 10; arr[6] = 45;
          // Zuweisung des Arrays arr an das neue Array newArr
          int[] newArr = arr;
          // Konsolenausgabe
          Console.WriteLine("newArr[0] = {0}", newArr[0]);
          Console.WriteLine("newArr[1] = {0}", newArr[1]);
          Console.WriteLine("newArr[2] = {0}", newArr[2]);
          Console.WriteLine("newArr[3] = {0}", newArr[3]);
          Console.WriteLine("newArr[4] = {0}", newArr[4]);
          Console.WriteLine("newArr[5] = {0}", newArr[5]);
          Console.WriteLine("newArr[6] = {0}", newArr[6]);
          Console.ReadLine();
        }
      }
    }
Zunächst wird das Array arr mit spezifischen Daten initialisiert, danach wird mit
    int[] newArr = arr;
das Array newArr deklariert. Das Besondere ist, dass die Initialisierung nicht mit new oder literal erfolgt, sondern durch die Zuweisung des Array-Bezeichners arr. Anschließend werden die Elemente des Arrays newArr der Reihe nach an der Konsole ausgegeben:
    newArr[0] = 72
    newArr[0] = 23
    newArr[2] = 24
    ...
Da die Laufzeitumgebung Arrays als Objekte behandelt, wird der Name eines Arrays ohne die zusätzliche Angabe eines Index der Startadresse des Arrays gleichgesetzt. Die Angabe arr steht somit für unsere fiktiv angenommene Adresse 4000, die nun auch zur Startadresse des Arrays newArr wird. Deshalb werden mit den Indizes des Arrays newArr dieselben Elemente angesprochen wie mit dem Array arr. ### 2.5.4 Mehrdimensionale Arrays Die bisher behandelten Arrays können Sie sich als eine einfache Folge von Daten auf einer Geraden vorstellen. Sie werden als eindimensionale Arrays bezeichnet. Zur Darstellung komplexer Datenstrukturen, beispielsweise räumlicher, sind eindimensionale Arrays aber nicht besonders gut geeignet. Daher kommen in der Praxis auch häufig zweidimensionale oder noch höher dimensionierte Arrays zum Einsatz. Ein zweidimensionales Array kann man sich als Matrix oder Tabelle vorstellen. Bekanntermaßen ist jede Zelle einer Tabelle eindeutig durch die Position in einer Reihe und einer Spalte identifizierbar. Um den Inhalt einer Tabellenzelle durch ein bestimmtes Array-Element zu beschreiben, bietet sich ein zweidimensionales Array an: Eine Dimension beschreibt die Reihe, die andere Dimension die Spalte. Angenommen, eine Tabelle hat vier Reihen und drei Spalten, dann könnte die Deklaration
    int[,] zelle = new int[4,3];
lauten. In Abbildung 2.10 ist dieser Sachverhalt anschaulich dargestellt. In der ersten Dimension, die die Reihen beschreibt, werden vier Elemente bereitgestellt und in der zweiten Dimension, die für die Spalte steht, drei. Damit enthält das Array insgesamt 4 * 3 = 12 Elemente, was auch der Anzahl der Zellen dieser Tabelle entspricht. Die Zuordnung, in der ersten Dimension die Zeilenanzahl und in der zweiten die Spaltenanzahl anzugeben, kann natürlich ebenso gut auch vertauscht werden. Hier klicken, um das Bild zu vergrößern Abbildung 2.10 Grafische Darstellung eines zweidimensionalen Arrays Zweidimensionale Arrays werden unter C# immer in der folgenden Weise deklariert:
    // Syntax: Deklaration eines zweidimensionalen Arrays
    Datentyp[,] Bezeichner;
Bei der Initialisierung werden die Dimensionsgrenzen festgelegt. Das kann explizit die Angabe der Dimensionsgröße sein, beispielsweise mit:
    int[,] point = new int[2,3];
Etwas schwieriger ist die literale Initialisierung eines mehrdimensionalen Arrays. Jede Dimensionsebene wird durch ein Paar geschweifter Klammern dargestellt, bei einem eindimensionalen Array also – wie oben eingangs gezeigt – durch ein Klammerpaar:
    {Anzahl der Elemente der ersten Dimension}
Da ein zweidimensionales Array als ein Feld zu verstehen ist, bei dem jedes Array-Element selbst wieder ein eigenes Feld gleichen Typs definiert, wird jedes Element der Initialisierung eines eindimensionalen Arrays durch ein Paar geschweifter Klammern ersetzt, in dem wiederum Werte des »Unterarrays« angegeben werden:
    {{Anzahl der Elemente der zweiten Dimension}, { }, ...}
Die literale Zuweisung an ein zweidimensionales Array könnte demnach wie folgt aussehen:
    int[,] point = new int[,]{{1,2,3},{4,5,6}}
Zulässig ist auch ebenfalls die kürzere Schreibweise mit:
    int[,] point = {{1,2,3},{4,5,6}}
Diese Systematik setzt sich mit jeder weiteren Dimension fort. Beispielhaft sei das noch an einem dreidimensionalen Array gezeigt:
    {{{Anzahl der Elemente der dritten Dimension}, { }, ...}, { },  ...}
Das folgende Codebeispiel zeigt anhand eines dreidimensionalen Arrays, dass die Initialisierung mit zunehmender Dimensionstiefe schon verhältnismäßig komplex und dazu auch noch schlecht lesbar ist:
    int[,,] myArr = { { {1,2,3,4},{3,4,5,6},{6,7,8,9}},
                      { {3,4,6,1},{6,19,3,4},{4,1,8,7}}
             };
Das Array myArr entspricht einem Array myArr[2,3,4]. Es weist in der dritten Dimension vier Elemente auf, in der zweiten drei und in der ersten zwei. Beim Zugriff auf ein mehrdimensionales Array muss man jede Dimension des entsprechenden Elements angeben. Beispielsweise schreibt die Anweisung
    Console.WriteLine(myArr[1,1,1]);
die Zahl 19 in das Konsolenfenster. ### 2.5.5 Array-Größe zur Laufzeit festlegen Nicht immer sind wir in der glücklichen Lage, schon zur Entwicklungszeit die Größe eines Arrays zu kennen, da diese sich möglicherweise erst zur Laufzeit ergibt. In dieser Situation kann die Festlegung der Größe auch über eine Variable erfolgen, die zur Laufzeit mit einem konkreten Wert initialisiert wird. Das folgende Beispiel demonstriert das. Die Aufgabenstellung soll dabei sein, jedem Array-Element als Wert das Quadrat seines Index zuzuweisen.
    // --------------------------------------------------------------
    // Beispiel: ...\Kapitel 2\ArrayInitialisierung
    // --------------------------------------------------------------
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    namespace ArrayInitialisierung {
      class Program {
        static void Main(string[] args) {
          int[] myArr;
          // Eingabe der Array-Größe
          Console.Write("Geben Sie die Azzahl der Elemente ein: ");
          int number = Convert.ToInt32(Console.ReadLine());
          // Initialisierung des Arrays
          myArr = new int[number];
          // jedes Element des Arrays in einer Schleife durchlaufen
          for(int i=0; i<number; i++) {
            // dem Array-Element einen Wert zuweisen
            myArr[i] = i * i;
            // das Array-Element an der Konsole ausgeben
            Console.WriteLine("myArr[{0}] = {1}",i, myArr[i]);
          }
          Console.ReadLine();
        }
      }
    }
Zuerst wird das Array myArr deklariert, dessen Größe zunächst noch unbestimmt ist. Im nächsten Schritt wird der Anwender zur Angabe der gewünschten Elementanzahl aufgefordert. Die Eingabe wird von der Methode ReadLine entgegengenommen und als Rückgabewert vom Typ string geliefert. Da wir das Array mit einem Integer initialisieren müssen, muss die Benutzereingabe vor der Zuweisung an die Variable number zuerst in den richtigen Typ konvertiert werden. Wir benutzen dazu wieder die Methode ToInt32 der Klasse Convert. Jetzt wissen wir, wie groß das Array myArr tatsächlich werden soll, und können es mit
    myArr = new int[number];
initialisieren. Thematisch noch nicht behandelt haben wir bisher Schleifen, um Anweisungen wiederholt auszuführen. Das soll uns aber in diesem Beispiel nicht davon abhalten, schon einmal einen kurzen Blick auf die for-Schleife zu werfen, die solche Anforderungen erfüllt. Die Anzahl der Schleifendurchläufe muss dabei vor dem Eintreten in die Schleife bekannt sein. Auf die Details der Syntax kommen wir in Abschnitt 2.7.1, »for-Schleife«, noch zu sprechen. In unserem Beispiel wird die Schleife vom ersten Index (= 0) bis zum letzten Index durchlaufen, der erst zur Laufzeit der Anwendung festgelegt wird. Innerhalb des Anweisungsblocks wird anforderungsgerecht zuerst das Quadrat des Index ermittelt und das Ergebnis dem entsprechenden Array-Element zugewiesen. Anschließend erfolgt die Ausgabe an der Konsole. Wenn Sie zur Laufzeit auf Aufforderung hin die Zahl 4 eingeben, wird im Fenster der Eingabekonsole die folgende Ausgabe erscheinen:
    myArr[0] = 0
    myArr[1] = 1
    myArr[2] = 4
    myArr[3] = 9
### 2.5.6 Bestimmung der Array-Obergrenze Es kommt häufig vor, dass Sie zur Laufzeit die Array-Obergrenze ermitteln müssen, bei einem mehrdimensionalen Array vielleicht sogar die Obergrenze einer bestimmten Dimension. Insbesondere bei Arrays, deren Größe (ähnlich wie im vorhergehenden Abschnitt gezeigt) erst zur Laufzeit festgelegt wird, kommt dieser Fragestellung besondere Bedeutung zu. Da ein Array ein Objekt ist, können auf dem Array-Bezeichner Methoden aufgerufen werden. Dazu gehört auch die Methode GetLength, die uns für jede beliebige Dimension eines vorgegebenen Arrays die Anzahl der Elemente zurückliefert. Auch wenn wir thematisch jetzt ein wenig vorgreifen, sollten wir uns kurz die Definition dieser Methode in der Klassenbibliothek ansehen:
    public int GetLength(int dimension)
Der Zugriffsmodifizierer public interessiert uns an dieser Stelle noch nicht. In einem anderen Zusammenhang werden wir uns mit ihm noch genau beschäftigen. Die Methode liefert einen int als Resultat zurück, gekennzeichnet durch die entsprechende Angabe vor dem Methodenbezeichner. In den runden Klammern ist ebenfalls ein int deklariert. Hier erwartet die Methode von uns die Angabe, von welcher Dimension wir die Elementanzahl, also die Größe, erfahren wollen. Dabei gilt, dass die erste Dimension mit 0 angegeben wird, die zweite mit 1 usw. Haben wir ein zweidimensionales Array mit
    int[,] myArr = new int[20,45];
deklariert, wird uns die Anweisung
    Console.WriteLine(myArr.GetLength(1));
die Größe der zweiten Dimension ausgeben, also 45. ### 2.5.7 Gesamtanzahl der Array-Elemente Liegt ein mehrdimensionales Array vor, können wir die Gesamtanzahl der Elemente ermitteln, indem wir die Methode GetLength auf jeder Dimension aufrufen und anschließend die Rückgabewerte multiplizieren – aber es geht auch anders. Die Klasse Array bietet mit der Eigenschaft Length die Möglichkeit, auf einfache Art und Weise an die gewünschte Information zu gelangen:
    int[,] myArr = new int[20,45];
    Console.WriteLine(myArr.Length);
Die Ausgabe dieses Codefragments wird 900 sein, denn das Array enthält insgesamt 20 * 45 Elemente. Bei einem eindimensionalen Array wird uns Length ebenfalls die Anzahl der Elemente liefern. In Schleifen, die für jedes Array-Element einmal durchlaufen werden sollen, benötigen wir jedoch meist den letzten Index des Arrays. Dieser ist um genau eins niedriger als der Wert, der von Length zurückgegeben wird, also:
    letzterArrayIndex = Array-Bezeichner.Length – 1;
### 2.5.8 Verzweigte Arrays In allen bisherigen Ausführungen hatten unsere Arrays eine rechteckige Struktur. In C# haben Sie aber auch die Möglichkeit, ein Array zu deklarieren, dessen Elemente selbst wieder Arrays sind. Ein solches Array wird als verzweigtes Array bezeichnet. Da die Anzahl der Dimensionen eines verzweigten Arrays für jedes Element unterschiedlich groß sein kann, ist ein solches Array äußerst flexibel. Die Deklaration und Initialisierung eines verzweigten Arrays ist nicht mehr so einfach wie die eines herkömmlichen mehrdimensionalen Arrays. Betrachten wir dazu zunächst ein Beispiel:
    int[][] myArray = new int[4][];
Das Array myArray enthält insgesamt vier Elemente, die ihrerseits wieder Arrays sind. Kennzeichnend für verzweigte Arrays ist die doppelte Angabe der rechteckigen Klammern sowohl links vom Gleichheitszeichen bei der Deklaration als auch rechts bei der Initialisierung. Im ersten Moment mag das verwirrend erscheinen, aber vergleichen wir doch einmal: Würden wir ein eindimensionales Array deklarieren und initialisieren, müsste die Anweisung dazu wie folgt lauten:
    int[] myArray = new int[4];
Durch das Hinzufügen einer zweiten Klammer, sowohl im deklarierenden als auch im initialisierenden Teil, machen wir deutlich, dass jedes Array-Element seinerseits ein Array repräsentiert. Hätten wir es mit einem einfachen Array zu tun, würde dieses als initialisiert gelten. Nun ist der Sachverhalt aber anders, denn jedes Element eines verzweigten Arrays muss seinerseits selbst initialisiert werden. Bezogen auf das oben deklarierte Array myArray könnte das beispielsweise wie folgt aussehen:
    myArray[0] = new int[3];
    myArray[1] = new int[4];
    myArray[2] = new int[2];
    myArray[3] = new int[5];
Wenn die einzelnen Elemente aller Arrays bekannt sind, kann alternativ auch literal mit
    myArray[0] = new int[3]{1,2,3};
    myArray[1] = new int[4]{1,2,3,4};
    myArray[2] = new int[2]{1,2};
    myArray[3] = new int[5]{1,2,3,4,5};
oder mit
    int[][] myArray = {new int[]{1,2,3},
                new int[]{1,2,3,4},
                new int[]{1,2},
                new int[]{1,2,3,4,5}};
initialisiert werden. Beim Zugriff auf das Element eines verzweigten Arrays muss zuerst berücksichtigt werden, in welchem Unterarray sich das gewünschte Element befindet. Danach wird die Position innerhalb des Unterarrays bekannt gegeben. Angenommen, Sie möchten den Inhalt des fünften Elements im Unterarray mit dem Index 3 auswerten, würde auf dieses Element wie folgt zugegriffen:
    Console.WriteLine(myArray[3][4]);
Verzweigte Arrays sind nicht nur auf eindimensionale Arrays beschränkt, sondern können auch mit mehrdimensionalen kombiniert werden. Benötigen Sie zum Beispiel ein verzweigtes, zweidimensionales Array, müssen Sie das sowohl im Deklarations- als auch im Initialisierungsteil berücksichtigen. In jedem Teil dient die jeweils zweite eckige Klammer zur Angabe der Dimensionsgröße:
    int[][,] myArray = new int[2][,];