Arrays gehören zu den wichtigsten Grundbausteinen in der Programmierung mit C und C++. Sie ermöglichen es, mehrere Werte desselben Datentyps effizient in einem zusammenhängenden Speicherbereich abzulegen. Gerade bei numerischen Berechnungen, Systemprogrammierung, Embedded-Software oder performanten Algorithmen spielen Arrays eine zentrale Rolle. In diesem Artikel erhältst du einen strukturierten Überblick über eindimensionale, zweidimensionale und mehrdimensionale Arrays, das Speicherlayout, typische Fehlerquellen sowie moderne Alternativen in C++.
Ein eindimensionales Array ist die einfachste Form eines Arrays. Es speichert mehrere Werte gleichen Typs in einer festen Reihenfolge und in einem zusammenhängenden Speicherbereich.
int numbers[5];
Optional kann das Array direkt mit Werten initialisiert werden:
int numbers[5] = {1, 2, 3, 4, 5};
Die Größe kann der Compiler auch automatisch aus der Initialisierung ableiten:
int numbers[] = {1, 2, 3, 4, 5};
Auf einzelne Werte wird über den Index zugegriffen. Die Zählung beginnt bei 0:
int value = numbers[2]; /* Zugriff auf das dritte Element */
Zweidimensionale Arrays eignen sich besonders gut zur Darstellung von Tabellen, Matrizen oder Spielfeldern.
int matrix[3][4];
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
int value = matrix[1][2]; /* Zeile 1, Spalte 2 */
C und C++ speichern zweidimensionale Arrays im sogenannten Row-Major-Format. Das bedeutet: Zuerst liegen alle Elemente der ersten Zeile im Speicher, danach die der zweiten Zeile usw.
matrix[0][0], matrix[0][1], matrix[0][2], matrix[1][0], ...
Arrays können auch drei oder mehr Dimensionen besitzen. Solche Strukturen werden zum Beispiel in der Bildverarbeitung, bei 3D-Simulationen oder in wissenschaftlichen Anwendungen verwendet.
int tensor[2][3][4];
Der Zugriff erfolgt entsprechend über mehrere Indizes:
tensor[1][2][3] = 42;
Mehrdimensionale Daten lassen sich auch dynamisch verwalten. In modernem C++ ist dabei meist eine Container-Lösung sauberer als rohe Zeigerketten.
int*** array = new int**[x];
Besser lesbar und oft sicherer ist meist eine moderne Lösung wie:
#include <vector>
std::vector<std::vector<int> > matrix;
Ein zentrales Merkmal klassischer Arrays ist der zusammenhängende Speicherbereich. Genau das macht Arrays schnell und effizient.
int arr[3] = {10, 20, 30};
Im Speicher könnte das schematisch so aussehen:
Adresse Wert
0x1000 10
0x1004 20
0x1008 30
Da alle Elemente direkt hintereinander liegen, kann die Adresse eines Elements sehr einfach berechnet werden.
address = base_address + (index * sizeof(type))
Für ein zweidimensionales Array ergibt sich daraus:
address = base + ((row * columns + col) * sizeof(type))
Die folgenden Grafiken zeigen, wie Arrays in C/C++ im Speicher organisiert sind. Entscheidend ist dabei, dass klassische Arrays zusammenhängend im Speicher abgelegt werden. Bei mehrdimensionalen Arrays erfolgt die Anordnung in C/C++ im Row-Major-Format, also zeilenweise.
Beispiel: int arr[5] = {10, 20, 30, 40, 50};
Alle Elemente liegen direkt hintereinander im Speicher. Da ein int hier beispielhaft 4 Byte groß ist, steigt die Adresse jeweils um 4 Byte.
Beispiel: int matrix[2][3] = { {1, 2, 3}, {4, 5, 6} };
| Spalte 0 | Spalte 1 | Spalte 2 | |
|---|---|---|---|
| Zeile 0 | 1matrix[0][0]0x2000 |
2matrix[0][1]0x2004 |
3matrix[0][2]0x2008 |
| Zeile 1 | 4matrix[1][0]0x200C |
5matrix[1][1]0x2010 |
6matrix[1][2]0x2014 |
Die Darstellung zeigt gut das Row-Major-Prinzip: Zuerst wird die komplette erste Zeile gespeichert, danach die zweite Zeile.
Beispiel: int cube[2][2][3];
Ein 3D-Array kann man sich als mehrere 2D-Schichten vorstellen. Die erste Dimension wählt die Schicht, die zweite die Zeile und die dritte die Spalte.
Schicht 0: cube[0][row][col]
| Spalte 0 | Spalte 1 | Spalte 2 | |
|---|---|---|---|
| Zeile 0 | 10[0][0][0]0x3000 |
20[0][0][1]0x3004 |
30[0][0][2]0x3008 |
| Zeile 1 | 40[0][1][0]0x300C |
50[0][1][1]0x3010 |
60[0][1][2]0x3014 |
Schicht 1: cube[1][row][col]
| Spalte 0 | Spalte 1 | Spalte 2 | |
|---|---|---|---|
| Zeile 0 | 70[1][0][0]0x3018 |
80[1][0][1]0x301C |
90[1][0][2]0x3020 |
| Zeile 1 | 100[1][1][0]0x3024 |
110[1][1][1]0x3028 |
120[1][1][2]0x302C |
Auch beim 3D-Array bleibt der Speicher linear. Intern werden die Daten also nicht als „echter Würfel“, sondern als fortlaufender Speicherblock abgelegt. Die Indizes werden nur zur logischen Strukturierung verwendet.
Arrays und Pointer sind in C und C++ eng miteinander verbunden, aber nicht identisch.
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;
Der Zugriff über Index und Pointer-Arithmetik ist oft äquivalent:
arr[2] == *(ptr + 2);
sizeof(arr); /* gesamte Arraygröße */
sizeof(ptr); /* Größe eines Zeigers */
Gerade bei Funktionsübergaben ist dieser Unterschied wichtig, weil ein Array-Parameter dort typischerweise als Pointer behandelt wird.
Arrays können je nach Deklaration in unterschiedlichen Bereichen des Speichers liegen.
void func() {
int arr[10];
}
int* arr = (int*)malloc(10 * sizeof(int));
Oder in klassischem C++:
int* arr = new int[10];
static int arr[10];
Arrays profitieren stark von der Cache-Architektur moderner Prozessoren, weil ihre Daten zusammenhängend im Speicher liegen.
for (int i = 0; i < 1000; i++) {
arr[i] += 1; /* effizient */
}
Weniger günstig ist ein unregelmäßiges Zugriffsmuster:
for (int i = 0; i < 1000; i++) {
arr[random_index[i]] += 1;
}
for (int i = 0; i < n; ++i) {
/* Verarbeitung */
}
Auch Pointer-Iteration kann in bestimmten Low-Level-Szenarien nützlich sein:
for (int* p = arr; p < arr + n; ++p) {
*p += 1;
}
std::vector bevorzugen
int arr[5];
arr[5] = 10; /* Fehler: gültig sind nur arr[0] bis arr[4] */
int arr[5]; /* enthält bei lokaler Deklaration undefinierte Werte */
int* arr = malloc(10); /* falsch */
Richtig ist:
int* arr = malloc(10 * sizeof(int));
In modernem C++ sind rohe Arrays nicht immer die beste Wahl. Häufig bieten Standardcontainer mehr Sicherheit und Komfort.
#include <array>
std::array<int, 5> arr = {1, 2, 3, 4, 5};
#include <vector>
std::vector<int> vec = {1, 2, 3, 4, 5};
Vorteile moderner Container:
std::vectorat()Arrays sind eine fundamentale Datenstruktur in C und C++. Sie bieten eine sehr hohe Performance, direkten Speicherzugriff und ein klares, lineares Layout im RAM. Gerade in systemnaher Programmierung, Embedded-Systemen, numerischen Verfahren und performanten Anwendungen sind sie unverzichtbar.
Wer Arrays wirklich versteht, versteht zugleich wichtige Grundlagen wie Speicheradressierung, Pointer-Arithmetik, Cache-Lokalität und Speicherverwaltung. Genau dieses Wissen bildet eine wichtige Basis für effiziente und professionelle Softwareentwicklung in C und C++.