11.2 Initialisierung und Zugriff auf Arrays
In dem folgenden Listing wird gezeigt, wie ein Array mit Werten initialisiert wird und wie darauf zugegriffen werden kann:
/* array1.c */ #include <stdio.h> #include <stdlib.h> int main(void) { int i[5]; /* Array mit 5 int-Elementen */ /* Wertzuweisungen des Arrays */ i[0] = 5; i[1] = 100; i[2] = 66; i[3] = 77; i[4] = 1500; /*Ausgabe der einzelnen Array-Elemente*/ printf("Array-Element i[0]= %d\n", i[0]); printf("Array-Element i[1]= %d\n", i[1]); printf("Array-Element i[2]= %d\n", i[2]); printf("Array-Element i[3]= %d\n", i[3]); printf("Array-Element i[4]= %d\n", i[4]); return EXIT_SUCCESS; }
Bei diesem Beispiel wurde an alle fünf Feldelemente ein Wert mithilfe des Indizierungsoperators [] übergeben. Und wie der Name des Operators schon sagt, dient dieser dem indizierten Zugriff auf Datentypen, die typischerweise hintereinander im Speicher abgelegt sind.
Warum lautet der Index des letzten Elements [4] und nicht [5]? Für den Computer ist die Zahl 0 auch ein Wert, und somit fängt dieser stets bei 0 an zu zählen:
Abbildung 11.2 Ein Array mit Werten initialisieren
Sie sehen hier 5 Zahlen: 0, 1, 2, 3 und 4. Befände sich im Programm die Zeile
i[5] = 111; printf("i[5] = %d\n",i[5]);
würde versucht, auf einen nicht reservierten Speicher zuzugreifen. Es wurde aber nur Speicher für fünf Adressen vom Datentyp int reserviert. Gefährlicher kann das werden, wenn dies in einer for-Schleife geschieht. Wird hier der Indexbereich überschritten, kann es passieren, dass mit falschen Werten weitergearbeitet wird. Hier ein Beispiel für einen Fehler, der leider oft gemacht wird:
/* array2.c */ #include <stdio.h> #include <stdlib.h> int main(void) { int test[10]; int i; for(i = 0; i <= 10; i++) /* !!Bereichsüberschreitung!! */ test[i] = i; for(i = 0; i <= 10; i++) printf("%d, ", test[i]); printf("\n"); return EXIT_SUCCESS; }
Das Programm macht nichts anderes, als das Array test[10] mit 11(!) Werten zu initialisieren, und anschließend werden diese Werte auf dem Bildschirm ausgegeben. Haben Sie den Fehler schon gefunden? Der Fehler liegt in der for-Schleife:
for(i = 0; i <= 10; i++)
Die for-Schleife wird insgesamt elfmal durchlaufen: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10. Es kann (oder besser es wird) bei diesem Programm passieren, dass test[10] tatsächlich den Wert 10 enthält.
Sobald aber irgendwo im Programm diese Speicheradresse für eine andere Variable benötigt wird, wird der aktuelle Wert von test[10] überschrieben. Es kann also nicht garantiert werden, dass der Wert von test[10] erhalten bleibt, was zwangsläufig zu ungewollten Effekten und schwer aufzuspürenden Fehlern im Programm führt.
Wenn Sie das Programm gegenwärtig ausführen wollen, ohne dass solch ein Fehler auftritt, müssen Sie nur den Zuweisungsoperator aus den beiden for-Schleifen entfernen:
for(i = 0; i < 10; i++) /* ohne '='-Zeichen richtig */
In dem Programm haben Sie gesehen, wie auf ein Array zugegriffen werden kann, um es mit Werten zu initialisieren:
for(i = 0; i < 10; i++) test[i] = i;
Damit wird die Schleife abgebrochen, sobald i den Wert 10 erreicht. Generell ist also bei der Verwendung von Arrays im Zusammenhang mit Schleifen Vorsicht geboten, und Sie müssen genau darauf achten, dass der Wertebereich des Feldes nicht unter- bzw. überschritten wird. Solche Unter- oder Überschreitungen werden vom Compiler nicht überprüft oder moniert.
Hinweis |
Auf manchen Systemen gibt es eine Compiler-Option (range-checking), womit ein solcher Über- bzw. Unterlauf eines Arrays zur Laufzeit des Programms geprüft wird. Das fertige Programm sollte allerdings nicht mehr mit dieser Option übersetzt werden, da dies zu einem schlechten Laufzeitverhalten führt. |
Statt einer konstanten Ganzzahl wurde hier die Variable i verwendet. Das funktioniert deshalb, weil diese Variable vom Datentyp int ist und somit auch einer Ordinalzahl entspricht. Die Variable wird von der for-Schleife bei jedem Durchlauf um den Wert eins erhöht (inkrementiert). Daraus ergibt sich, dass als Index nur Ganzzahlwerte erlaubt sind. Oftmals wird zur Bezeichnung des Index auch eine define-Konstante verwendet, wie das folgende Beispiel demonstriert:
/* array3.c */ #include <stdio.h> #include <stdlib.h> #define SIZE 10 int main(void) { int zahlen[SIZE] = { 0 }; printf("Anz. Elemente : %d\n", sizeof(zahlen) / sizeof(int)); return EXIT_SUCCESS; }
Solche Konstanten können die Lesbarkeit bei längeren Programmen erheblich verbessern und tragen dazu bei, Fehler zu vermeiden. Ein weiterer Vorteil entsteht, wenn Sie die Anzahl der Elemente des Arrays erhöhen wollen. Sie müssen nur den Wert der define-Konstante ändern und nicht mühsam im Programm danach suchen.
Arrays lassen sich auch anders, nämlich direkt bei der Deklaration, initialisieren. Die Werte müssen dabei zwischen geschweiften Klammern stehen:
int numbers[] = { 1, 2, 4, 5, 9 };
Wenn Sie das Array so initialisieren, können Sie die Größe des Arrays auch weglassen. C kümmert sich darum, dass genügend Speicher zur Verfügung steht. Die einzelnen Initializer werden immer mit einem Komma getrennt und stehen in geschweiften Klammern. Dadurch ist das Feld wie folgt mit Werten belegt:
numbers[0] = 1; numbers[1] = 2; numbers[2] = 4; numbers[3] = 5; numbers[4] = 9;
Natürlich können Sie trotzdem die Größe des Arrays angeben. Bei einem größeren Array hat diese Initialisierung den Vorteil, dass Sie alle anderen Werte gleich mit 0 vorbelegen können. Anstatt eine for-Schleife zu schreiben, wie etwa:
int bigarray[1000]; for(i = 0; i < 1000; i++) bigarray[i] = 0;
lässt sich das auch einfacher formulieren:
int bigarray[1000] = { 0 };
Hier wurde nur das Array mit dem Index [0], also bigarray[0], mit dem Wert 0 initialisiert. Die restlichen 999, die nicht ausdrücklich initialisiert wurden, werden jedoch automatisch ebenfalls mit dem Wert 0 besetzt.
Dies kann aber noch ganz anders gelöst werden, und zwar unter Verwendung der folgenden Funktion:
/* Beschreibung der Funktion, siehe Abschnitt 20.8 */ #include <string.h> void *memset(void *adres, int zeichen, size_t n);
Mit der Funktion memset() wird der Wert von zeichen in jedes der ersten n Zeichen des Speicherbereichs mit der Adresse adres geschrieben. Das sieht dann wie folgt aus:
int bigarray[1000]; memset(bigarray, 0, sizeof(bigarray));
Wenn Sie das jetzt nicht nachvollziehen können: Ein paar Seiten später werden Sie es besser verstehen. Tatsächlich handelt es sich hier auch um einen Spezialfall der Verwendung von memset(), da sich dieses Verfahren nur mit dem Wert 0 auf ein int-Array anwenden lässt. Das liegt daran, dass memset() bitweise arbeitet. Bei einem int-Array auf einem 32-Bit-System würde dies bei 4 Bytes den Wert 16843009 bedeuten (alle Bits auf 1).
Hier noch ein weiteres Beispiel zur Initialisierung von Arrays:
double inhalt[100] = { 2.4, 2.8, 9.45, 10.99 };
Internes |
memset() kann unter Umständen schneller sein als eine for-Schleife, da memset() viel näher an der Hardware operiert und eventuell deren Funktionalität ausbeuten kann (z. B. ist es viel sinnvoller, einen char[512] mit 128 0-longs zu belegen, anstatt 512-mal eine 0 zu speichern). memset() funktioniert außerdem auch bei multidimensionalen Arrays ohne Pointer, z. B.: char m_ar[10][10]; memset(m_ar, 0, 100); |
Hiermit wurden folgende Werte initialisiert:
inhalt[0] = 2.4 inhalt[1] = 2.8 inhalt[2] = 9.45 inhalt[3] = 10.99 inhalt[4] = 0.0 inhalt[5] = 0.0 ... inhalt[97] = 0.0 inhalt[98] = 0.0 inhalt[99] = 0.0
Ab inhalt[4] bis inhalt[99] werden alle Werte automatisch mit 0.0 initialisiert. Leider ist es nicht möglich, den Inhalt eines Arrays mit einem anderen konstanten Wert außer 0 zu initialisieren.
Hinweis |
Manche Systeme vertragen keine lokalen, übergroß dimensionierten Arrays. Sollte das Programm bei Ihnen gleich nach dem Start abstürzen und verwenden Sie ein recht großes Array, dann könnte ein global definiertes Array Abhilfe schaffen. |
11.2.1 Gültigkeitsbereich von Arrays
Der Gültigkeitsbereich von Arrays richtet sich danach, ob es sich dabei um ein statisches, globales oder ein normales (lokales) Array handelt. Betrachten Sie zur Verdeutlichung ein kleines Beispielprogramm:
/* array4.c */ #include <stdio.h> #include <stdlib.h> int wert_global[5]; int main(void) { static int wert_static[5]; int wert_auto[5]; int i; for(i = 0; i < 5; i++) printf("%d:\t%10d\t%10d\t%10d\n", i, wert_global[i], wert_static[i], wert_auto[i] ); return EXIT_SUCCESS; }
Abbildung 11.3. zeigt die Ausgabe des Programms am Bildschirm.
Abbildung 11.3 Gültigkeitsbereich von Variablen
Das Programm gibt für das globale und für das mit dem Schlüsselwort static deklarierte Array jeweils den Wert 0 aus. Das automatische Array wert_auto hingegen gibt einen undefinierten Wert zurück. Daraus lässt sich schließen, dass globale und mit static deklarierte Arrays automatisch mit 0 initialisiert werden.
Das Verhalten des Programms ändert sich, wenn die Automatic-Variable (wert_auto) mit mindestens einem Wert initialisiert wird:
/* array5.c */ #include <stdio.h> #include <stdlib.h> int wert_global[5]; int main(void) { static int wert_static[5]; int wert_auto[5] = { 0 }; /* Array mit 0 initialisiert */ int i; for(i = 0; i < 5; i++) printf("%d:\t%10d\t%10d\t%10d\n", i, wert_global[i], wert_static[i], wert_auto[i] ); return EXIT_SUCCESS; }
Es wurde hier nur die Zeile
int wert_auto[5] = { 0 };
verändert und wert_auto[0] mit dem Wert 0 initialisiert. Die Ausgabe des Programms zeigt jetzt (erwartungsgemäß) die initialisierten Werte.
Abbildung 11.4 Gültigkeitsbereich von Variablen
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.