Arrays

  • Was ist ein Array?
  • Wie definiert man ein Array?
  • Initialisieren von Arrays
  • Lesen aus und Schreiben in ein Array
  • Wie funktioniert ein Array?

Was ist ein Array?

Man spricht von einem Array, wenn mehrere Dinge eines Typs zusammengepackt werden. Vielleicht kennt man den Begriff aus dem Film „Contact“, wo das Very Large Array ein Zusammenschluss vieler Radioteleskopantennen ist. Auch bei LED-Lampen werden mehrere einzelne Leuchtdioden in einem Array angeordnet. Auch in der Programmierung ist ein Array ein Zusammenschluss vieler Dinge gleichen Datentyps.

Vieles lässt sich als Zusammenfassung mehrerer Daten gleichen Typs ausdrücken. Ein Koordinate eines Pixels auf dem Bildschirm hat eine X- und eine Y-Position: ein Array von zwei Integern. Der Pixel hat eine Farbe, die sich aus den Intensitäten für die Farben Rot, Grün und Blau zusammensetzen: drei Integerwerte. Und dem nächsten Beispiel für ein Array werde ich nachher ein ganzes Kapitel widmen: Ein Text besteht aus vielen Buchstaben.

Arrays sind also elementar wichtig in der Programmierung, deswegen schauen wir sie uns genauer an.

Wie definiert man ein Array?

Ein Array stellt eine Erweiterung eines Datentyps dar. Möchte man beispielsweise mehrere Integer zu einem Array zusammenschließen, so gibt man hinter dem Identifier-Namen in eckigen Klammern an, wieviele Objekte man von diesem Typ haben möchte:

  int eins;
  int vier[4];

Hier haben wir also einen Identifier eins vom Typ int und einen Identifier vier vom Typ int[4], vier ist also viermal so groß wie eins.

Arrays kann man aus jedem beliebigen Datentyp erstellen:

  float array[10]
  char  text[10];

Initialisieren von Arrays

Arrays kann man wie Variablen sofort initialisieren. Hierfür schreibt man die Werte mit Kommata getrennt in geschweifte Klammern.

  int eins = 1;
  int vier[4] = { 1111, 2222, 3333, 4444 };

Wichtig hierbei ist, dass die Anzahl der Werte nicht die Größe des Arrays übersteigt. In der Regel richtet sich die Größe des Arrays allerdings daran, wieviele Daten wir unterbringen wollen. In dem Fall können wir den Computer auch selbst zählen lassen, indem wir die Anzahl zwischen den eckigen Klammern einfach weglassen:

  int vier[] = { 1111, 2222, 3333, 4444 };

Lesen und Schreiben in einem Array

Nehmen wir nun an, dass wir ein sehr großes Array haben und es einfach zuviel Aufwand ist, es wie gerade gezeigt zu initialisieren. Wir wissen ja bereits, dass sich C um die Initialisierung von Variablen nicht kümmert, also kümmert es sich auch nicht um die Initialisierung von vielen Variablen. Wenn wir sicherstellen wollen, dass wir bekannte Anfangswerte im Array haben, bevor wir es erstmals benutzen, so müssen wir es selbst initialisieren. Hierfür bietet sich natürlich eine Schleife an:

int main( void )
{
  int array[1000];
 
  for( int i = 0; i < 1000; i++ )
    array[i] = 0; 
 
  return 0; 
}

Wir sehen hier, dass man sich aus der Array-Variablen mit den eckigen Klammern ein ganz bestimmtes Objekt heraussuchen kann und dann wie eine normale Variable darauf zugreifen kann. Alle Objekte des Arrays liegen hintereinander im Speicher und in den eckigen Klammern geben wir nun einfach den Index der Variablen an, die wir lesen oder beschreiben wollen. Der []-Operator dereferenziert aus dem Array ein einzelnes Objekt, so dass wir anschließend nicht mehr mit einem Array arbeiten, sondern mit einem Objekt des Arrays - bei unserem Integer-Array ist das entsprechend ein int.

int object = array[ 17 ];

array ist eine Aneinanderreihung von 1000 Integers und wir weisen object das 18. Integer zu. Warum das 18., wenn in der Klammer doch eine 17 steht? Nun das erste Integer im Array hat den Index 0, das zweite den Index 1 und so weiter.

In allen modernen Programmiersprachen beginnt man für ein Array bei 0 an zu zählen. Ein Array mit 1000 Elementen geht also vom 0. Element bis zum 999. Index 1000 steht also für das 1001. Element - und so groß ist unser Array nicht, wir würden hier also einen Fehler begehen. Der C-Programmierer hat absolut freie Hand, was er programmiert und wenn er über die Größe eines Arrays hinweg geht, dann führt das zu willkürlichen Fehlern. Es ist ein typischer Anfängerfehler, die Grenzen der verwendeten Arrays nicht sauber zu beachten. Achtet darauf!

In den eckigen Klammern wird der Index übergeben und dieser muss ja abzählbar sein. Es darf dort jede beliebige Expression stehen, solange der Datentyps Integer wieder int ergibt. Dass es eine Expression ist bedeutet, dass wir eine Konstante, wie in diesem Beispiel 17, angeben können, eine Variable, wie i im Beispiel zuvor oder auch eine Formel zur Berechnung des Indexes wird akzeptiert, zum Beispiel:

int object = array[ i + 2 ];

Achtet darauf, dass der Index auch negativ sein darf. So kann man auch vor den Startpunkt des Arrays greifen - das will man meistens nicht. Es gibt aber auch Fälle, in denen man vor den Startpunkt springen möchte. Hierfür verwendet man dann Zeiger, die möglicheriweise auf ein Element inmitten des Arrays zeigen. Diese werden wir in einem späteren Kapitel kennenlernen.

Wie funktioniert ein Array?

Das Kapitel könnte hier schon enden und tut es in den meisten Büchern an dieser Stelle auch. Denn wenn man eine Programmiersprache lernen möchte, dann ist an dieser Stelle gesagt, was es zum Thema Arrays zu sagen gibt. Wir wollen aber Programmieren lernen. Das heißt, dass wir uns auch ein wenig damit auseinander setzen wollen, was der Computer da eigentlich tut. Grundsätzlich ist die Sache sehr einfach, nehmen wir das Array vier von vorhin und schauen uns die Sache im Speicher an: Hier liegen einfach vier Integers hintereinander im Speicher. 1)

  int vier[4] = { 1111, 2222, 3333, 4444 };


Adresse 8000: 1111 Adresse 8004: 2222 Adresse 8008: 3333 Adresse 8012: 4444

Der Identifier vier zeigt dabei ganz einfach auf das erste Objekt. Der Identifier vier speichert also nicht die ganzen Werte innerhalb des Arrays, sondern merkt sich nur, wo das erste Objekt im Speicher liegt - alle anderen Integers liegen schließlich im Speicher in einer Reihe dahinter. Wenn das Array, wie hier im Beispiel angegeben, an der Speicherstelle 8000 beginnt, dann liegt das nächste Objekt an Adresse 8004. Ein Integer benötigt (auf den meisten Systemen) 4 Bytes, entsprechend liegt das erste Integer an den Adressen 8000, 8001, 8002 und 8003, das zweite Integer beginnt also bei Adresse 8004.

Der []-Operator bekommt einen Integerwert als Index übergeben und dieser wird ganz einfach mit der Größe eines Objektes multipliziert - ein Integer ist 4 Byte groß - und anschließend auf die Startadresse des Arrays addiert.

Startadresse des Arrays gewünschter Index Rechnung Position im Speicher
8000 0 8000 + 0 * 4 8000
8000 1 8000 + 1 * 4 8004
8000 2 8000 + 2 * 4 8008
8000 3 8000 + 3 * 4 8012
8000 index 8000 + index * 4

Hat man ein Array eines anderen Datentyps, sieht die Rechnung entsprechend anders aus. Ein einzelner char benötigt nur ein Byte. Entsprechend ist die Berechnung Startadresse + index * 1.

Der sizeof-Operator

Benötigt man die Größe eines Datentyps kann man C auch mit dem sizeof-Operator direkt fragen:

printf( "Ein Int benötigt %d Bytes\n", sizeof( int ));
printf( "Ein Char benötigt %d Bytes\n", sizeof( char ));

sizeof() liefert die Anzahl der benötigten Bytes zurück und das ist natürlich eine abzählbare Menge, also ein Integer, das wir auch entsprechend verarbeiten können.

Mit sizeof kann man auch die Größe eines Arrays in Bytes bestimmen. Wenn man wissen möchte, wieviele Elemente ein Array besitzt, muss man durch die Größe des Datentypes teilen. Beispiel: Ein int-Array, das 16 Byte groß ist, geteilt durch die Anzahl der Bytes pro einzelnem int-Element (4 Byte) ergibt die Anzahl der Elemente im Array: 4.

Schauen wir uns das im Quelltext an:

#include <stdio.h>
#include <string.h>
 
int main(void)
{
  char text[] = "Hello proggen.org";
  int array[] = { 111, 222, 333, 444 };
 
  printf( "Groesse des Textes: %d Bytes / Elemente\n", sizeof( text ));
  printf( "Laenge des Textes: %d Zeichen\n", strlen( text ) );
  printf( "Groesse von array: %d Bytes\n", sizeof( array ));
  printf( "Elemente in array: %d Elemente\n", sizeof( array ) / sizeof( int ));
 
  return 0;  
}

Wir erhalten folgende Ausgabe:

Groesse des Textes: 18 Bytes / Elemente
Laenge des Textes: 17 Zeichen
Groesse von array: 16 Bytes
Elemente in array: 4 Elemente

Da ein char 1 Byte groß ist, müssen wir hier natürlich nicht mehr durch 1 teilen. Sehr schön sieht man den Unterschied zwischen dem sizeof()-Operator und der Funktion strlen(): strlen() zählt die Zeichen vor dem Nullbyte: 17 Zeichen, während sizeof() die Länge des Arrays inklusive des abschließenden Nullbytes zurückliefert.

sizeof hat gegenüber strlen den Vorteil, dass es ermittelt wird, während der Compiler läuft. Wenn das kompilierte Programm also gestartet müssen die Buchstaben nicht mehr gezählt werden, sondern die Länge des Arrays steht bereits fest und ist in das Programm einkompiliert. Das spart Rechenzeit.

Ziel dieser Lektion

Du solltest eine Vorstellung von einem Array haben, es initialisieren, lesen und beschreiben können. Und Du solltest eine Vorstellung haben, wie ein Array im Arbeitsspeicher liegt. In der kommenden Lektion werden wir Arrays anwenden und die hier gewonnenen theoretischen Grundlagen praktisch anwenden.

Wir beschäftigen uns in der kommenden Lektion mit Strings, das sind Buchstaben-Arrays, Zeichenketten. Kurz: Texte.

1)
Weitere Informationen zum Thema Arbeitsspeicher findest Du hier.