13.10 Mehrdimensionale Arrays initialisieren
 
Werte bei mehrdimensionalen Arrays werden ähnlich übergeben wie bei einem eindimensionalen Array. Hier zum Beispiel eine Deklaration mit sofortiger Initialisierung von Werten:
/* 4 Zeilen 5 Spalten */
int Matrix[4][5] = { {10,20,30,40,50},
{15,25,35,45,55},
{20,30,40,50,60},
{25,35,45,55,65}};
Dadurch ergibt sich folgende Belegung des Feldes:
 Hier klicken, um das Bild zu Vergrößern
Abbildung 13.7
Ein zweidimensionales Array, mit Werten initialisiert
Wie bei den normalen Arrays lassen sich die einzelnen Elemente mithilfe des Feldindex initialisieren. Wollen Sie beispielsweise das Element mit dem Wert 60 ändern in 100, dann geht das wie folgt:
Matrix[2][4] = 100;
Hier wurde zum Beispiel der Inhalt von Matrix[2][4] verändert. Wollen Sie das Element mit dem Wert 65 umändern in 66, dann wird folgender Feldindex verwendet:
Matrix[3][4] = 66;
Eine weitere Möglichkeit zur Initialisierung von mehrdimensionalen Arrays ist folgende:
int Matrix[4][4] = { {0},
{1},
{0,1},
{0,0,1} };
Hiermit besitzen alle Feldelemente, die nicht ausdrücklich initialisiert wurden, automatisch den Wert 0. Die Belegung des Feldes sieht also folgendermaßen aus:
 Hier klicken, um das Bild zu Vergrößern
Abbildung 13.8
Ein zweidimensionales Array (4 x 4)
In der Praxis werden mehrdimensionale Arrays bei verschiedensten Arten von Berechnungen benötigt oder bei 2-D-Darstellungen von Grafiken. Das folgende Programm demonstriert die Anwendung eines mehrdimensionalen Arrays:
/* md_array1.c */
#include <stdio.h>
#include <stdlib.h>
#define VOL1 3 /* Anzahl Felder erste Dimension – Zeilen */
#define VOL2 4 /* Anzahl Felder zweite Dimension – Spalten */
int main(void) {
int i,j;
int myarray[VOL1][VOL2]; /* [3][4] */
/* Eingabe der Array-Elemente */
for(i=0; i < VOL1; i++) {
for(j=0; j < VOL2; j++) {
printf("Wert für myarray[%d][%d]:", i, j);
scanf("%d", &myarray[i][j]);
}
}
printf("\nAusgabe von myarray[%d][%d]...\n\n", VOL1, VOL2);
for(i=0; i < VOL1; i++) {
for(j=0; j < VOL2; j++) {
printf("\t%4d ",myarray[i][j]);
}
printf("\n\n");
}
return EXIT_SUCCESS;
}
Das Programm nimmt nichts anderes vor, als den Anwender nach Ganzzahlen abzufragen, um diese Werte im zweidimensionalen Array zu speichern und wieder auszugeben. Um dies zu realisieren, wird eine äußere und innere for-Schleife verwendet. Die äußere for-Schleife dient dabei der Inkrementierung der Variablen im linken Indizierungsoperator (oder, aus der Sicht einer Tabellenkalkulation, der Zeile). Die innere for-Schleife inkrementiert den Wert im rechten Indizierungsoperator (und somit der Spalte).
Im nächsten Beispielprogramm soll ein kleines Zeitkonto für einen Arbeitgeber verwaltet werden. Damit sollen einige Arbeitszeitberechnungen durchgeführt werden.
/* md_array2.c */
#include <stdio.h>
#include <stdlib.h>
#define ARBEITER 3
#define TAGE 5
int zeitkonto[ARBEITER][TAGE];
/* Fehlerausgabe */
void error(int n) {
printf("%d (?) Falsche Eingabe!!\n",n);
}
/* –1- Ausgabe der Wochenarbeitszeit je Arbeiter */
void ArbeiterWochenStunden(void) {
int i,j,tmp;
for(i=0; i < ARBEITER; i++) {
tmp=0;
printf("Wochenarbeitszeit von Arbeiter Nr. %d\n", i+1);
printf("-------------------------------------\n");
for(j=0; j < TAGE; j++) {
printf("|%d Std.", zeitkonto[i][j]);
tmp += zeitkonto[i][j];
}
printf("| = Ges. %d Std.\n\n",tmp);
}
}
/* –2- Durchschnittszeiten pro Tag in der Woche je Arbeiter */
void ArbeiterTagesDurchschnitt(void) {
int i,j,tmp;
for(i=0; i < ARBEITER; i++) {
tmp=0;
printf("Durchschn. pro Tag/Woche Arbeiter: %d\n",i+1);
printf("-------------------------------------------\n");
for(j=0; j < TAGE; j++) {
tmp+=zeitkonto[i][j];
}
printf("Durchschn. v. Arbeiter %d p. Tag: %.1f "
"Std/Tag\n\n" , i+1, (float)tmp / TAGE);
}
}
/* –3- Durchschnittszeit aller Arbeiter pro Tag */
void TeamTagesDurchschnitt(void) {
int i,j,tmp;
for(i=0; i < TAGE; i++) {
tmp=0;
printf("Durchschn. Arbeitszeit aller Mitarbeiter pro "
"Tag %d = ", i+1);
for(j=0; j < ARBEITER; j++) {
tmp += zeitkonto[j][i];
}
printf("%.1f Std.\n\n",(float)tmp/ARBEITER);
}
}
/* –4- Gesamtstunden aller Arbeiter in der Woche */
void TeamWochenStunden(void) {
int i, j, tmp=0;
printf("Gesamtstunden aller Arbeiter in der Woche\n");
printf("-----------------------------------------\n");
for(i=0; i < ARBEITER; i++) {
for(j=0; j < TAGE; j++) {
tmp+=zeitkonto[i][j];
}
}
printf("Gesamtstunden aller Arbeiter i. d. Woche: "
" %d Std.\n" , tmp);
}
/* Stundenübersicht eines einzelnen Arbeiters */
void ArbeiterStundenUebersicht(void) {
int arb,tag;
printf("Welcher Arbeiter: ");
scanf("%d", &arb);
printf("Welcher Tag: ");
scanf("%d", &tag);
if(arb > ARBEITER) {
printf("Die Firma hat nur %d Arbeiter\n", ARBEITER);
return;
}
else if(tag > TAGE) {
printf("Es werden nur %d Tage gespeichert\n", TAGE);
return;
}
printf("Arbeiter Nr.%d hat am Tag %d : ", arb, tag);
printf("%d Stunden gearbeitet!\n\n", zeitkonto[arb-1][tag-1]);
}
int main(void) {
int abfrage, i, j;
for(i=0; i < TAGE; i++) {
printf("\n\tTag %d in der Woche\n",i+1);
printf("\t-------------------\n\n");
for(j=0; j < ARBEITER; j++) {
printf("Arbeiter Nr.%d in Std.: ",j+1);
scanf("%d",&zeitkonto[j][i]);
if(zeitkonto[j][i] > 24)
printf("Ein Tag hat nur 24 Stunden?\n");
}
}
do {
printf("\n\n");
printf("\t-1- Stundenwoche\n");
printf("\t-2- Durchschnitt/Tag\n");
printf("\t-3- Durchschnitt aller Arbeiter/Tag\n");
printf("\t-4- Stunden aller Arbeiter/Woche\n");
printf("\t-5- Einzelauswahl eines Arbeiters\n");
printf("\t-6- ENDE\n");
printf("\n\tIhre Wahl : ");
scanf("%1d",&abfrage);
printf("\n");
switch(abfrage) {
case 1 : ArbeiterWochenStunden();
break;
case 2 : ArbeiterTagesDurchschnitt();
break;
case 3 : TeamTagesDurchschnitt();
break;
case 4 : TeamWochenStunden();
break;
case 5 : ArbeiterStundenUebersicht();
break;
case 6 : break;
default : error(abfrage);
}
} while(abfrage != 6);
return EXIT_SUCCESS;
}
Die Bildschirmausgabe des Programms könnte zum Beispiel folgendermaßen aussehen (siehe Abbildung 13.9).
Es fällt auf, dass die Funktionen immer in etwa gleich aufgebaut sind. Auf entsprechende korrekte Feldindexierung muss natürlich geachtet werden. In der Funktion ArbeiterStundenUebersicht() wird demonstriert, wie gezielt auf ein Element eines Arrays zugegriffen werden kann. Das Programm ist natürlich noch verbesserungswürdig. Warnungen, dass ein Arbeiter zu viel oder zu wenig arbeitet, ob ein Arbeiter krank war oder die Stunden als Gleitkommazahlen angegeben werden sollen, sind nur einige Vorschläge dazu.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 13.9
Stundenverwaltung des Personals in Aktion
Tatsächlich sind Arrays zwar sehr komfortabel in der Anwendung, sie sind jedoch sehr unflexibel, was die Anzahl der Elemente angeht. Die Anzahl der Elemente muss zum Zeitpunkt der Implementierung schon festgelegt werden, da sich ein Feld nicht ohne Mehraufwand dynamisch zur Laufzeit des Programms vergrößern oder verkleinern lässt.
Das bedeutet, dass die Menge der im Array zu speichernden Daten schon vor Ablauf des Programms bekannt sein oder zumindest überdimensioniert werden muss.
Wenn das Array im vorgestellten Beispiel für 1000 Mitarbeiter dimensioniert würde, wäre das Programm nicht mehr benutzbar, sobald mehr als 1000 Mitarbeiter verwaltet werden sollen.
Eine Lösungsmöglichkeit besteht darin, das Array sehr groß zu dimensionieren, um von vorneherein sehr große Grenzen vorzugeben, etwa MitarbeiterArray[100000].
Dieser Ansatz kostet aber sehr viel (möglicherweise) ungenutzten Arbeitsspeicher, der das Programm unter Umständen stark verlangsamt.
Da aus Performance-Gründen generell stets möglichst wenig Arbeitsspeicher von Programmen belegt werden soll, gelten Arrays bei großen Datenmengen oder stark wechselnder Anzahl der Daten als nicht so effizient wie etwa verkettete Listen.
In Kapitel 23, Dynamische Datenstrukturen, wird auf die Datenverwaltung mit verketteten Listen näher eingegangen.
13.10.1 Tic Tac Toe
 
Ein weiteres interessantes Beispiel zur Demonstration von zweidimensionalen Arrays ist das wohl allseits bekannte Spiel »Tic Tac Toe«. Sie benötigen dabei lediglich ein Kästchen von 3 x 3 Feldern. Dies lässt sich prima mit einem zweidimensionalen Array darstellen: eine Dimension für die Reihe und eine weitere für die Spalte.
char TicTacToe[3][3] = { {' ',' ',' '},
{' ',' ',' '},
{' ',' ',' '} };
Ein kurze Beschreibung des Spiels: Ein Spieler hat das Zeichen X und ein anderer das Zeichen O. Nach einem Zug ist der andere Spieler an der Reihe. Gewonnen hat der Spieler, der zuerst drei gleiche Zeichen (X oder O) in der Waagerechten, in der Senkrechten oder in der Diagonalen hat. Es gibt insgesamt acht Stellungsmöglichkeiten, um das Spiel zu gewinnen. Diese gilt es zu überprüfen. Es gibt außerdem noch eine neunte Möglichkeit, nämlich die, dass alle Felder besetzt sind, und keiner der beiden Spieler hat gewonnen. Hier der vollständige Quellcode dazu:
/* tictactoe.c */
#include <stdio.h>
#include <stdlib.h>
#ifdef __unix__
#define clrscr() printf("\x1B[2J")
#elif __BORLANDC__ && __MSDOS__
#include <conio.h>
#elif __WIN32__ || _MSC_VER
#define clrscr() system("cls")
#else
#define clrscr() printf("clrscr() – Fehler!!\n")
#endif
#define X 'X'
#define O 'O'
#define LEER ' '
#define GAME_OVER 0
#define A_WINNER 1
#define CONTINUE 2
/* Inhalt des 3 x 3 grossen Felds */
char TicTacToe[3][3] = { {' ',' ',' '},
{' ',' ',' '},
{' ',' ',' '} };
/* Spieler1 hat das Zeichen 'X' */
char Spieler1 = X;
/* Spieler2 hat das Zeichen 'O' */
char Spieler2 = O;
/* Anzahl der Felder, die besetzt werden können */
unsigned int felder = 9;
/* Funktionsprototypen */
void print_spielfeld(void);
char neuer_zug(char);
int if_win(void);
/* Gibt den aktuellen Zustand des Spielfelds aus */
void print_spielfeld(void) {
int i;
clrscr();
printf(" 1 2 3 \n +---+---+---+\n");
for(i = 0; i < 3; i++) {
printf(" %d | ",i+1);
printf("%c",TicTacToe[i][0]);
printf(" | ");
printf("%c",TicTacToe[i][1]);
printf(" | ");
printf("%c",TicTacToe[i][2]);
printf(" | \n");
if(i != 2) {
printf(" +---+---+---+\n");
}
else {
printf(" +---+---+---+\n");
}
}
}
/* Führt einen neuen Zug aus.
* char ch: Zeichen des Spielers, der an der Reihe ist, 'X'
* oder 'O'
* Rückgabewert: Zeichen des Spielers, der eben an der Reihe war
* falls ein Feld besetzt ist, wird der Rückgabewert vertauscht,
* damit der aktuelle Spieler nochmals seinen Zug machen kann.
* Hat ein Spieler gewonnen, gibt die Funktion die
* symb. Konstante GAME_OVER zurück
*/
char neuer_zug(char ch) {
unsigned int row, colum;
printf("\nSpieler \"%c\" ist an der Reihe\n\n",ch);
printf("Zeile (1–3): ");
scanf("%d",&row);
printf("Spalte (1–3): ");
scanf("%d",&colum);
if(TicTacToe[row-1][colum-1] == LEER) {
/* Zeichen in das mehrdim. Array */
TicTacToe[row-1][colum-1] = ch;
print_spielfeld();
/* Haben wir schon einen Gewinner? */
if(if_win() == A_WINNER)
return GAME_OVER;
}
else { /* Ein bereits besetztes Feld */
print_spielfeld();
printf("\n!!! Feld ist bereits gesetzt !!!\n");
return (ch == X) ?O :X;
}
/* Sind bereits alle Felder besetzt? */
if(--felder > 0)
return ch;
else {
printf("\nAlle Felder sind besetzt – Unentschieden\n");
return GAME_OVER;
}
}
/* Auswertung aller Möglichkeiten, um einen Gewinner zu ermitteln
* Rückgabewert: symb. Konstante A_WINNER falls ein Gewinner
* ermittelt wurde oder die symb. Konstante CONTINUE zum
* Weiterspielen.
*/
int if_win(void) {
/* Zuerst Spieler1 'X' */
if(TicTacToe[0][0] == Spieler1 &&
TicTacToe[0][1] == Spieler1 &&
TicTacToe[0][2] == Spieler1 ||
TicTacToe[1][0] == Spieler1 &&
TicTacToe[1][1] == Spieler1 &&
TicTacToe[1][2] == Spieler1 ||
TicTacToe[2][0] == Spieler1 &&
TicTacToe[2][1] == Spieler1 &&
TicTacToe[2][2] == Spieler1 ||
TicTacToe[0][0] == Spieler1 &&
TicTacToe[1][0] == Spieler1 &&
TicTacToe[2][0] == Spieler1 ||
TicTacToe[0][1] == Spieler1 &&
TicTacToe[1][1] == Spieler1 &&
TicTacToe[2][1] == Spieler1 ||
TicTacToe[0][2] == Spieler1 &&
TicTacToe[1][2] == Spieler1 &&
TicTacToe[2][2] == Spieler1 ||
TicTacToe[0][0] == Spieler1 &&
TicTacToe[1][1] == Spieler1 &&
TicTacToe[2][2] == Spieler1 ||
TicTacToe[0][2] == Spieler1 &&
TicTacToe[1][1] == Spieler1 &&
TicTacToe[2][0] == Spieler1) {
printf("Spieler1 hat gewonnen\n");
return A_WINNER;
}
/* Jetzt Spieler2 'O' */
else if( TicTacToe[0][0] == Spieler2 &&
TicTacToe[0][1] == Spieler2 &&
TicTacToe[0][2] == Spieler2 ||
TicTacToe[1][0] == Spieler2 &&
TicTacToe[1][1] == Spieler2 &&
TicTacToe[1][2] == Spieler2 ||
TicTacToe[2][0] == Spieler2 &&
TicTacToe[2][1] == Spieler2 &&
TicTacToe[2][2] == Spieler2 ||
TicTacToe[0][0] == Spieler2 &&
TicTacToe[1][0] == Spieler2 &&
TicTacToe[2][0] == Spieler2 ||
TicTacToe[0][1] == Spieler2 &&
TicTacToe[1][1] == Spieler2 &&
TicTacToe[2][1] == Spieler2 ||
TicTacToe[0][2] == Spieler2 &&
TicTacToe[1][2] == Spieler2 &&
TicTacToe[2][2] == Spieler2 ||
TicTacToe[0][0] == Spieler2 &&
TicTacToe[1][1] == Spieler2 &&
TicTacToe[2][2] == Spieler2 ||
TicTacToe[0][2] == Spieler2 &&
TicTacToe[1][1] == Spieler2 &&
TicTacToe[2][0] == Spieler2) {
printf("Spieler2 hat gewonnen\n");
return A_WINNER;
}
return CONTINUE;
}
int main(void) {
char check = X;
/* Leeres Spielfeld ausgeben */
print_spielfeld();
do { /* War Spieler mit dem Zeichen 'X' gerade dran ... */
/* ... dann ist jetzt Spieler mit dem Zeichen 'O' dran */
if(check==X) {
check=neuer_zug(O);
}
else { /* ... ansonsten der Spieler mit dem Zeichen 'X' */
check=neuer_zug(X);
}
} while( check != GAME_OVER );
return EXIT_SUCCESS;
}
 Hier klicken, um das Bild zu Vergrößern
Abbildung 13.10
Das Spiel Tic Tac Toe für die Konsole
Wenn Sie jetzt noch Lust und viel Zeit haben, können Sie sich ja hinsetzen und eine Funktion basteln, um gegen den Computer antreten zu können, also eine eigene KI programmieren. Dabei können Sie so ähnlich vorgehen wie bei der Funktion if_win(), nur dass Sie auch handeln müssen. Sie sollten aber dabei beachten, dass Sie die KI so programmieren, dass der menschliche User auch noch gewinnen kann.
Hinweis KI steht für künstliche Intelligenz. Gemeint ist damit die Möglichkeit einer Maschine, sich Funktionen des Menschen, wie Denken oder Bewegungsabläufe, mithilfe von Programmen anzueignen (simulieren).
|
13.10.2 Dreidimensionale Arrays
 
Zur Veranschaulichung ein weiteres Beispiel, wie ein dreidimensionales Array direkt mit Werten initialisiert werden kann:
int dreid[2][3][4]={{{6,7,4,3},{6,4,6,9},{3,4,6,7}},
{{7,8,6,4},{5,99,3,5},{4,6,7,8}}};
Hier ist eine geschweifte Klammer hinzugekommen:
int dreid[][][]= {1.Feldindex{2.Feldindex{3.Feldindex}}};
Wenn zum Beispiel auf die erste Zahl zugegriffen werden soll, geschieht das folgendermaßen:
dreid[0][0][0] /* erste Zahl 6 */
Oder auf die Zahl 99:
dreid[1][1][1] /* die Zahl 99 */
Ein Beispiel dazu erspare ich mir, da Sie in der Regel selten mit einem solchen Array zu tun haben. Außerdem lässt sich so etwas nur recht schwer vorstellen.
|