Pointer und mehrdimensionale Arrays in C meistern
In der C-Sprache ist ein Array eine Sammlung von Werten gleichen Typs, die in zusammenhängenden Speicherbereichen gespeichert sind. Jedes Element in einem Array (eindimensional oder mehrdimensional) wird durch einen oder mehrere eindeutige ganzzahlige Indizes identifiziert.
Ein Pointer hingegen speichert die Adresse einer Variable. Die Adresse des 0. Elements in einem Array ist der Pointer des Arrays. Man kann den „Dereferenzierungsoperator“ nutzen, um auf den Wert zuzugreifen, auf den ein Pointer verweist.
Man kann eindimensionale, zweidimensionale oder mehrdimensionale Arrays in C deklarieren. Der Begriff „Dimension“ bezieht sich auf die Anzahl der Indizes, die zur Identifikation eines Elements in einer Sammlung erforderlich sind.
Pointer und eindimensionale Arrays
In einem eindimensionalen Array wird jedes Element durch einen einzelnen Integer identifiziert:
int a[5] = {1, 2, 3, 4, 5};
Hier ist die Zahl „1“ am Index 0, „2“ am Index 1, usw.
Eine Variable, die die Adresse des 0. Elements speichert, ist sein Pointer:
int *x = &a[0];
Einfach ausgedrückt: Der Name des Arrays zeigt auch auf die Adresse des 0. Elements. Man kann also auch diesen Ausdruck nutzen:
int *x = a;
Beispiel
Da der Wert des Pointers um die Größe des Datentyps inkrementiert, bewegt x++ den Pointer zum nächsten Element im Array.
#include <stdio.h>
int main(){
int arr[] = {1, 2, 3, 4, 5};
int length = sizeof(arr) / sizeof(arr[0]);
int i = 0;
int *ptr = arr;
while (i < length){
printf("arr[%d]: %d \n", i, *(ptr + i));
i++;
}
return 0;
}
Ausgabe
arr[0]: 1
arr[1]: 2
arr[2]: 3
arr[3]: 4
arr[4]: 5
Pointer und zweidimensionale Arrays
Wenn ein eindimensionales Array wie eine Liste von Elementen ist, ist ein zweidimensionales Array wie eine Tabelle oder Matrix.
Die Elemente in einem 2D-Array können logisch in Zeilen und Spalten angeordnet betrachtet werden. Daher wird die Position jedes Elements durch zwei Indizes bestimmt: Zeilennummer und Spaltennummer. Beide Indizes beginnen bei „0“.
int arr[2][2];
Ein solches Array wird logisch so dargestellt:
| Col0 | Col1 | Col2 | |
|---|---|---|---|
| Row0 | arr[0][0] | arr[0][1] | arr[0][2] |
| Row1 | arr[1][0] | arr[1][1] | arr[1][2] |
| Row2 | arr[2][0] | arr[2][1] | arr[2][2] |
Zu beachten: Die tabellarische Anordnung ist nur eine logische Repräsentation. Der Compiler alloziert einen Block zusammenhängender Bytes. In C erfolgt die Array-Allokation zeilenweise (row-major), d. h. die Elemente werden zeilenweise in das Array eingelesen.
Hier deklarieren wir ein 2D-Array mit drei Zeilen und vier Spalten (die Zahl in der ersten eckigen Klammer bezieht sich immer auf die Zeilenanzahl):
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
Der Compiler alloziert den Speicher für das obige 2D-Array zeilenweise. Angenommen, das erste Element liegt bei Adresse 1000 und die Größe von int beträgt 4 Bytes, erhalten die Elemente folgende Speicheradressen:
| Row 0 | Row 1 | Row 2 | |
|---|---|---|---|
| Value | 1 2 3 4 | 5 6 7 8 | 9 10 11 12 |
| Address | 1000 1004 1008 1012 | 1016 1020 1024 1028 | 1032 1036 1040 1044 |
Wir weisen die Adresse des ersten Elements des Arrays num dem Pointer ptr mittels Adressoperator & zu:
int *ptr = &arr[0][0];
Beispiel 1
Wenn der Pointer um 1 inkrementiert wird, bewegt er sich zur nächsten Adresse. Alle 12 Elemente im „3x4“-Array können in einer Schleife wie folgt zugegriffen werden:
#include <stdio.h>
int main(){
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
};
// Pointer ptr zeigt auf Array num
int *ptr = &arr[0][0];
int i, j, k = 0;
// Elemente des Arrays num via Pointer ptr ausgeben
for (i = 0; i < 3; i++){
for (j = 0; j < 4; j++){
printf("%d ", *(ptr + k));
k++;
}
printf("\n");
}
return 0;
}
Ausgabe
1 2 3 4
5 6 7 8
9 10 11 12
Im Allgemeinen kann die Adresse jedes Elements des Arrays mit folgender Formel berechnet werden:
Adresse von Element an i-ter Zeile und j-ter Spalte = baseAddress + [(i * anzahl_spalten + j) * sizeof(array_type)]
In unserem 3x4-Array:
Adresse von arr[2][2] = 1000 + (2*4 + 2)*4 = 1044
Man kann sich auf die obige Tabelle beziehen – sie bestätigt, dass die Adresse von „arr[2][2]“ 1044 ist (Anm.: im Originaltext stand arr[3][4], korrigiert auf 0-basiert).
Beispiel 2
Nutzen wir den Dereferenzierungs-Pointer, um den Wert an der Adresse zu holen. Wir verwenden die Formel, um das Array mit seinem Pointer zu traversieren:
#include <stdio.h>
int main(){
// 2d array
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
int ROWS = 3, COLS = 4;
int i, j;
// pointer
int *ptr = &arr[0][0];
// Element des Arrays via Pointer ptr ausgeben
for (i = 0; i < ROWS; i++){
for (j = 0; j < COLS; j++) {
printf("%4d ", *(ptr + (i * COLS + j)));
}
printf("\n");
}
return 0;
}
Ausgabe
1 2 3 4
5 6 7 8
9 10 11 12
Pointer und dreidimensionale Arrays
Ein dreidimensionales Array ist ein Array von zweidimensionalen Arrays. Ein solches Array wird mit drei Subscripts deklariert:
int arr[x][y][z];
Dieses Array kann als „x“ Anzahl an Layern von Tabellen betrachtet werden, wobei jede Tabelle „y“ Zeilen und „z“ Spalten hat.
Ein Beispiel für ein 3D-Array:
int arr[3][3][3] = {
{ {11, 12, 13}, {14, 15, 16}, {17, 18, 19} },
{ {21, 22, 23}, {24, 25, 26}, {27, 28, 29} },
{ {31, 32, 33}, {34, 35, 36}, {37, 38, 39} },
};
Ein Pointer auf das 3D-Array kann deklariert werden als:
int *ptr = &arr[0][0][0];
Da der Name des Arrays selbst die Adresse des 0. Elements ist, können wir den Pointer eines 3D-Arrays auch so schreiben:
int *ptr = arr;
Jeder Layer aus „x“ Zeilen und „y“ Spalten belegt:
x * y * sizeof(data_type) Bytes.
Angenommen, der dem 3D-Array „arr“ allozierte Speicher beginnt bei Adresse 1000, startet der zweite Layer (mit i = 1) bei 1000 + (3 * 3) * 4 = 1036 Byte-Position.
ptr = Basisadresse des 3D-Arrays arr
Wenn JMAX die Zeilenanzahl und KMAX die Spaltenanzahl ist, dann ist die Adresse des Elements an der 0. Zeile und 0. Spalte des 1. Slices:
arr[1][0][0] = ptr + (1 * JMAX * KMAX)
Die Formel für den Wert eines Elements an der j-ten Zeile und k-ten Spalte des i-ten Slices lautet:
arr[i][j][k] = *(ptr + (i * JMAX * KMAX) + (j * KMAX + k))
Beispiel: 3D-Array mittels Pointer-Dereferenzierung ausgeben
Nutzen wir diese Formel, um das 3D-Array mit Pointer-Dereferenzierung auszugeben:
#include <stdio.h>
int main(){
int i, j, k;
int arr[3][3][3] = {
{ {11, 12, 13}, {14, 15, 16}, {17, 18, 19} },
{ {21, 22, 23}, {24, 25, 26}, {27, 28, 29} },
{ {31, 32, 33}, {34, 35, 36}, {37, 38, 39} },
};
int JMAX = 3, KMAX = 3;
int *ptr = arr; // &arr[0][0][0];
for(i = 0; i < 3; i++){
for(j = 0; j < 3; j++){
for(k = 0; k < 3; k++){
printf("%d ", *(ptr + (i * JMAX * KMAX) + (j * KMAX + k)));
}
printf("\n");
}
printf("\n");
}
return 0;
}
Ausgabe
11 12 13
14 15 16
17 18 19
21 22 23
24 25 26
27 28 29
31 32 33
34 35 36
37 38 39
Im Allgemeinen ist der Zugriff auf ein Array mit einem Pointer dem Zugriff mit Subscript-Darstellung recht ähnlich. Der Hauptunterschied: Die subscriptierte Deklaration alloziert Speicher statisch, während Pointer für dynamische Speicherallokation genutzt werden können.
Um ein mehrdimensionales Array an eine Funktion zu übergeben, muss man Pointer statt Subscripts verwenden. Allerdings ist die Nutzung eines subscriptierten Arrays bequemer als Pointer, die für neue Lernende schwierig sein können.
C Sprache
- C-Programmierung meistern: Umfassendes PDF-Tutorial für Einsteiger
- Guru99: Umfassende Online-Tutorials & Business-Software-Reviews
- C++ Call by Reference: Mit Zeigern – Praktische Beispiele
- C# Reflection: Laufzeit-Typinformation & dynamische Programmierung
- Unsicherer C#-Code meistern: Zeiger und Speicherverwaltung
- Datei-I/O in C meistern: Erstellen, Öffnen & Verwalten von Dateien
- Top 15 C++ Online-Kurse, um die Sprache zu meistern (2024 Update)
- C#-Interfaces meistern: Definition, Implementierung und praktische Anwendungsfälle
- Zeiger auf Zeichen und Funktionsverwendung in C meistern
- C# Collections meistern: Praktisches Tutorial mit Real-World-Beispielen