Algorithmen und Datenstrukturen 3.3 Mehrdimensionale Arrays W. Tasin, M.Sc. Fakultät 04 tasin@hm.edu
Allgemeines (1) Ein Feld (Array) kann seinerseits als Komponente wieder ein Array besitzen. Es wird ein zusammenhängender Speicherbereich reserviert, der als Matrix von n x m Werten betrachtet wird. /* Speicherbereich fuer 5x3 int-werte reservieren */ int vals[5][3]; /* Speicherbereich fuer 2x8 short-werte reservieren */ short aa[2][8]; /* Speicherbereich fuer 3x4 char-werte reservieren oder fuer drei ASCIIZ-Strings mit je max. 3 Zeichen (+ abschliessendem NUL-Character) */ char abbr[3][4]; Algorithmen und Datenstrukturen - W. Tasin, M.Sc. 2
Allgemeines (2) Da der Speicher nur eindimensional adressiert wird, wird ein mehrdimensionales Array intern in ein eindimensionales Array umgerechnet. Die Verwendung von mehrdimensionalen Arrays erhöht die Lesbarkeit und Wartbarkeit in mehrdimensionalen Aufgabenstellungen. #define ROWS 5 #define COLS 3 #define ROWS 5 #define COLS 3 /* 15 Werte eindimensional */ short vals [ROWS*COLS]; unsigned i, j; for (i=0; i<rows; i++) for (j=0; j<cols; j++) vals[i*cols+j]=i*10+j+1; /* 5 Zeilen, 3 Spalten*/ short vals [ROWS][COLS]; unsigned i, j; for (i=0; i<rows; i++) for (j=0; j<cols; j++) vals[i][j]=i*10+j+1; Algorithmen und Datenstrukturen - W. Tasin, M.Sc. 3
Allgemeines (3) #define ROWS 5 #define COLS 3 /* 5 Zeilen, 3 Spalten*/ short vals[rows][cols]; Logische Anordnung der Variablen Der erste Index gibt die Zeile an, der zweite die Spalte. vals[0]... vals[1]... vals[2]... vals[3]... vals[4]... [0] [1] [2] 1 2 3 11 12 13 21 22 23 31 32 33 41 42 43 vals[1][2] vals[4][1] Algorithmen und Datenstrukturen - W. Tasin, M.Sc. 4
Allgemeines (4) #define ROWS 5 #define COLS 3 /* 5 Zeilen, 3 Spalten*/ vals[0][0] vals[0][1] vals[0][2] 1 2 3 0xFF80FF82 0xFF80FF84 0xFF80FF86 1. Zeile short vals[rows][cols]; vals[1][0] vals[1][1] 11 12 0xFF80FF88 0xFF80FF8A 2. Zeile vals[1][2] 13 0xFF80FF8C vals[2][0] 21 0xFF80FF8E Physikalische Anordnung der Variablen im eindimensionalen Speicher. vals[3][2] vals[4][0] vals[4][1] 33 41 42 0xFF80FF98 0xFF80FF9A 0xFF80FF9C 5. Zeile vals[4][2] 43 0xFF80FF9E Algorithmen und Datenstrukturen - W. Tasin, M.Sc. 5
Komponentenzugriff (1) Nur durch Angabe des Variablennamens mit allen Indizes (außerhalb der Arraydefinition) erhält man den Wert der Komponente. Sobald ein Index fehlt, dann stellt der Ausdruck eine Adresse dar. Der Ausdruck ist seinerseits ein rvalue ihm kann nichts zugewiesen werden. short vals[5][3]; printf("%p\n", vals); printf("%p\n", vals[0]); printf("%p\n", vals[3]); Adresse rvalue printf("%d\n", vals[2][1]); vals[2][1] = 5; printf("%p\n", &vals[2][1]); short-komponente lvalue Adresse der Komponente rvalue Algorithmen und Datenstrukturen - W. Tasin, M.Sc. 6
Komponentenzugriff (2) Der Zugriff auf die Komponente kann auch über den Dereferenzierungsoperator (*) erfolgen. Jedoch muss im Ausdruck die Anzahl der Dereferenzierungsoperatoren der Anzahl der Dimensionen entsprechen, um auf die Komponente zurückgreifen zu können. short vals[5][3]; printf("%p\n", vals); printf("%p\n", *vals); printf("%p\n", *(vals+3)); Adresse rvalue printf("%d\n", *(*(vals+2)+1)); *(*(vals+2)+1) = 5; printf("%p\n", *(vals+2)+1); short-komponente lvalue Adresse der Komponente rvalue Algorithmen und Datenstrukturen - W. Tasin, M.Sc. 7
Arraydefinition und Initialisierung (1) Auch die Größe eines mehrdimensionalen Arrays muss lt. ANSI-89/90 bei der Arraydefinition zur Übersetzungszeit bekannt sein. Die Anzahl der Dimensionen spiegelt sich bei der Initialisierung durch die Anzahl der Klammern ({) am Anfang des Initialisierungsblocks wider. short vals[5][3] = {{1, 2, 3}, {11, 12, 13}, {21}}; Zeile 1 vals[0] 1 2 3 Zeile 2 vals[1] 11 12 13 Zeile 3 vals[2] 21 0 0 Zeile 4 vals[3] 0 0 0 Zeile 5 vals[4] 0 0 0 Für fehlende Initialisierungswerte wird 0 verwendet. Algorithmen und Datenstrukturen - W. Tasin, M.Sc. 8
Arraydefinition und Initialisierung (2) Nur die Größenangabe der 1. Dimension darf entfallen. Die Zeilenanzahl kann vom Compiler durch die Initialisierung ermittelt werden. short vals[][3] = {{1, 2, 3}, {11, 12, 13}, {21}, {31, 32}}; Zeile 1 vals[0] 1 2 3 Zeile 2 vals[1] 11 12 13 Zeile 3 vals[2] 21 0 0 Zeile 4 vals[3] 31 32 0 4 Zeilen wurden initialisiert 4 x 3 Werte Reservierung von 12 short-werten Auch hier gilt, dass die konkrete Größenangabe der 1. Dimension höhere Priorität hat, als die Festlegung durch den Initialisierungsblock. Algorithmen und Datenstrukturen - W. Tasin, M.Sc. 9
Arraydefinition und Initialisierung (3) Die ISO-Norm erlaubt auch die Initialisierung von mehrdimensionalen Array mit eindimensionalen Initialisierungswerten. Diese Art der Initialisierung sollte aber vermieden werden, da sie die Mehrdimensionalität des Arrays nicht mehr darstellt. short vals[][3] = {1, 2, 3, 11, 12, 13, 21, 22, 23}; Zeile 1 vals[0] 1 2 3 Zeile 2 vals[1] 11 12 13 Zeile 3 vals[2] 21 22 23 Der GNU-C Compiler liefert in diesem Fall eine Warnung. Deshalb ist hier die folgende Initialisierung zu favorisieren: short vals[][3] = {{1, 2, 3}, {11, 12, 13}, {21, 22, 23}}; Algorithmen und Datenstrukturen - W. Tasin, M.Sc. 10
Arraydefinition und Initialisierung (4) Für die Initialisierung von char-arrays als ASCIIZ-Stringarray können auch Stringkonstanten verwendet werden. char praeps[][4] = {{'i', 'c', 'h', ''}, {'d', 'u', ''}, {'e', 'r', ''}, {'s', 'i', 'e', ''}, {'e', 's', ''}}; oder char praeps[][4] = {"ich", "du", "er", "sie", "es"}; Zeile 1 praeps[0] 'i' 'c' 'h' '' Zeile 2 praeps[1] 'd' 'u' '' '' Zeile 3 praeps[2] 'e' 'r' '' '' Zeile 4 praeps[3] 's' 'i' 'e' '' Zeile 5 praeps[4] 'e' 's' '' '' Reservierung von 5x4 char-werten Algorithmen und Datenstrukturen - W. Tasin, M.Sc. 11
Arraydefinition und Initialisierung (5) ACHTUNG: Die Arraygrößen können nachträglich nicht verändert werden. char praeps[][4] = {"ich", "du", "er", "sie", "es"}; Es existiert im Beispiel nur eine Speicherreservierung für 5 Strings mit je max. 3 Buchstaben (+ abschließendem NUL-Character). #include <string.h> /*... */ strcpy(praeps[2], "hallo");!!! Der angegebene strcpy()-aufruf kompromittiert den Speicher der 4. Zeile: Zeile 1 praeps[0] 'i' 'c' 'h' '' Zeile 2 praeps[1] 'd' 'u' '' '' Zeile 3 praeps[2] 'h' 'a' 'l' 'l' Zeile 4 praeps[3] 'o' '' 'e' '' Zeile 5 praeps[4] 'e' 's' '' '' Algorithmen und Datenstrukturen - W. Tasin, M.Sc. 12
Zweidimensionales char-array vs. eindimensionales Zeigerarray (1) Vorgehensweise der Initialisierung: char praeps[5][4] = {"ich", "du", "er", "sie", "es"}; Zuerst wird ein Speicherbereich für 20 x sizeof(char) reserviert. Danach werden die Initialisierungswerte in diesen Speicherbereich hineinkopiert (evtl. werden zusätzl. Speicherstellen mit 0 initialisiert). {"ich", "du", "er", "sie", "es"} 0x09807F10 0x09807F14 0x09807F17 0x09807F1A 0x09807F1E i c h d u e r s i e e s 0xFFF0A802 0xFFF0A806 0xFFF0A80A 0xFFF0A80E 0xFFF0A812 praeps i c h d u e r s i e e s Algorithmen und Datenstrukturen - W. Tasin, M.Sc. 13
Zweidimensionales char-array vs. eindimensionales Zeigerarray (2) Vorgehensweise der Initialisierung von Zeigerarrays: char *ppraep[5] = {"ich", "du", "er", "sie", "es"}; Zuerst wird ein Speicherbereich für 5 x sizeof(char *) reserviert. Danach werden die Startadressen der ASCIIZ-Strings in diesen Speicherbereich hineinkopiert. {"ich", "du", "er", "sie", "es"} 0x09807F10 0x09807F14 0x09807F17 0x09807F1A 0x09807F1E i c h d u e r s i e e s 0xFFF0A802 0xFFF0A806 0xFFF0A80A 0x09807F1A 0x09807F1E ppraep 0x09807F10 0x09807F14 0x09807F17 0x09807F1A 0x09807F1E Algorithmen und Datenstrukturen - W. Tasin, M.Sc. 14
Übergabeparameter (1) Wird ein mehrdimensionales Array als Übergabeparameter verwendet, so werden keinerlei Arraykomponenten übergeben, sondern - wie im eindimensionalen Fall - nur die Adresse auf das erste Element der ersten Zeile. Damit der Funktion die logische Aufteilung des Arrays bekannt gemacht wird, ist - wie bei der Initialisierung - die Größenangabe der Spalten (und nachfolgender Dimensionen) nötig. Auf die Größenangabe der Zeile (1. Dimension) kann auch hier verzichtet werden. void outelements(unsigned rows, int a[][cols]) { } unsigned i, j; for (i=0; i<rows; i++) { for (j=0; j<cols; j++) printf("%d ", a[i][j]); } putchar('\n'); Algorithmen und Datenstrukturen - W. Tasin, M.Sc. 15
Übergabeparameter (2) Bei dem Parameter handelt es sich - wie im Fall des eindimensionalen Arrays - um einen Zeiger auf die erste Komponente. Jedoch kann die Typangabe des Parameters (int a[][cols]) nicht (wie beim eindimensionalen Array) in einen eindimensionalen Zeiger (int *) geändert werden, da sonst der logische Aufbau der Variablen verloren ginge. Der Komponentenzugriff über den Dereferenzierungsoperator (*) bleibt weiterhin möglich. void outelements(unsigned rows, int a[][cols]) { } unsigned i, j; for (i=0; i<rows; i++) { for (j=0; j<cols; j++) printf("%d ", (*(a+i))[j]); /* oder: *(a[i]+j) */ } putchar('\n'); Algorithmen und Datenstrukturen - W. Tasin, M.Sc. 16
Übergabeparameter (3) Da es sich bei Übergabeparameter immer um eine Wertkopie in eine Variable des Namensbereichs der Funktion handelt, wird aus einem rvalue (vals) ein lvalue (a). Somit kann der Wert von a geändert werden. Ein Inkrementieren des Werts in a bedeutet, dass a auf die nächste Zeile zeigt. Wert vals (konstante Adresse) wird in Variable a kopiert #define COLS 6 #define ROWS 5 int vals[rows][cols]; outelements(rows, vals); void outelements(unsigned rows, int a[][cols]) { unsigned i, j; for (i=0; i<rows; i++) { for (j=0; j<cols; j++) printf("%d ", (*a)[j]); a=a+1; /* Springt in die nächste Zeile */ putchar('\n'); } } Algorithmen und Datenstrukturen - W. Tasin, M.Sc. 17
n-dimensionale Arrays Die getroffenen Vereinbarungen gelten analog auch für n-dimensionale Arrays. #include <stdio.h> #include <string.h> #define GERMAN 0 #define ITALIAN 1 #define ENGLISH 2 #define LANGUAGES 3 #define MAX_LEN 10 int main() { } char translate[][languages][max_len+1]= {{"ich", "io", "I"}, {"du", "tu", "you"}, {"er", "lui", "he"}, {"sie", "lei", "she"}, {""}}; /*... */ return 0; Algorithmen und Datenstrukturen - W. Tasin, M.Sc. 18