Der Fehler ist ganz schoen tricky, und ich hab auch etwas gebraucht um es zu verstehen.
Mehrdimensionale Arrays sind nicht ganz so einfach im Umgang in C oder C++, desshalb verwende ich sie nie. C ist nicht Java, und mehrdimensionale Arrays sind ein Punkt, der nicht so einfach ist, vor allem wenn du jetzt eine dynamische Groesse von deinem Feld im Speicher abbilden willst.
Erst mal muss man feststellen wie mehrdimensionale Arrays in C im Speicher ueberhaupt ausschauen. Zum Testen gebe ich mir also die Adresse von bombenfeld selber aus (bombenfeld ist ein Pointer auf die Adresse des ersten Elements in der ersten Zeile) -- desweiteren gebe ich mir die Adresse des ersten Feldes aus der zweiten Zeile aus, und schaue mir dessen Speicheradresse an:
Code:
printf("%p / %p\n", bombenfeld, &bombenfeld[1][0]);
Bei mir gibt das folgendes aus:
0x7fff681e1100 / 0x7fff681e115c
in Dezimal ist das die Speicheradresse 3777755007410400 und 3777755007410534 d.h. eine Differenz von 206 Adressen. Das ist merkwuerdig, weil eine Zeile in meinem Beispiel ja nur 23 Spalten hat.
Letztendlich ist mein Verstaendnis jetzt, dass C mehrdimensionale Arrays nicht in zusammenhaengender Form im Speicher hinterlegt d.h. auf die Zellen der ersten Zeile muessen nicht sofort die Zellen der zweiten Zeile folgen.
Das ganze waere kein Problem, wenn du jetzt dein mehrdimensionales Array mit dynamischer Groesse nicht falsch an die einzelnen Funktionen uebergeben wuerdest, was letztendlich dein Fehler ist:
Code:
#define ZEILEN 26
#define SPALTEN 26
void feldFuellen([b]int feld[ZEILEN][SPALTEN][/b], size_t zeilen, size_t spalten, int bomben);
Du definierst Funktionen die ein mehrdimensionales Array mit fixer Groesse erwarten, naemlich 26 Zeilen und 26 Spalten. Wenn du diesen Funktionen jetzt einen Pointer auf ein mehrdimensionales Array uebergibst, das kleiner ist als 26 Zeilen und 26 Spalten, dann fuehrt das dazu, dass in diesen Funktionen auf das mehrdimensionale Array falsch zugegriffen wird, weil der Compiler davon ausgeht, dass dein Array 26 Zeilen und 26 Spalten hat. Das liegt eben daran, dass mehrdimensionale Arrays im Speicher nicht fortlaufend hinterlegt sind, und der Compiler abhaengig von der Array-Groesse unterschiedlich auf die Arrays zugreifen muss.
Wie greift C auf mehrdimensionale Arrays zu? Das koennen wir uns anschauen, indem wir uns die Assembler-Ausgabe z.B. von folgendem Programm anschauen:
Code:
void foo(int array[3][3]) {
array[2][2] = 42;
}
void bar(int array[5][5]) {
array[2][2] = 42;
}
Kompiliert mit
gcc -O0 -S foo.c bekommen wir folgenden Assembler-Code bei meinem 64 bit Intel System:
Code:
_foo:
Leh_func_begin1:
pushq %rbp
Ltmp0:
movq %rsp, %rbp
Ltmp1:
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
[b]movl $42, 32(%rax)[/b]
popq %rbp
ret
_bar:
Leh_func_begin2:
pushq %rbp
Ltmp2:
movq %rsp, %rbp
Ltmp3:
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
[b]movl $42, 48(%rax)[/b]
popq %rbp
ret
Merkst du etwas? foo schreibt auf das Array mit
movl $42, 32(%rax) und bar mit
movl $42, 48(%rax) d.h. obwohl auf C-Seite der Code mit array[2][2] = 42; genau der gleiche ist, unterscheidet sich auf Assembler-Seite der Zugriff auf das Array im Speicher, weil mit einem anderen Index auf die Arrays zugegriffen wird.
Das ist das Problem mit mehrdimensionalen Arrays. Ich sehe zwei moegliche Loesungsansaetze fuer dein Problem:
Loesung 1
Du hast die Groesse deines Feldes sowieso auf 26 Zeilen und Spalten beschraenkt. D.h. anstatt das bombenfeld mit dynamischer Groesse zu erzeugen:
Code:
int bombenfeld[zeilen][spalten];
Wuerde ich das Bombenfeld-Array einfach mit fixer Groesse im Speicher erstellen:
Code:
int bombenfeld[ZEILEN][SPALTEN];
Das ist die einfachste Loesung die du machen kannst. Dein Code funktioniert nun ohne Fehler, weil du den einzelnen Funktionen immer ein Array mit der korrekten Zeilen- und Spaltenanzahl uebergibst, die sie auch erwarten. Einziger Nachteil ist halt, dass ungenutzter Speicher verbraucht wird, den du bei kleineren Feldern gar nicht nutzt.
Loesung 2
Wenn du wirklich ein Feld dynamischer Groesse erstellen willst, dann muesstest du ein paar mehr Stellen im Code aendern, und typischerweise das Feld dynamisch mit malloc auf dem Heap erstellen. Ich weiss nicht ob das schon deinem Wissensstand von 1-Jahr C-Kurs gerecht wird.
Ich habe frueher Computerspiele gebastelt mit 2D-Feldern, in denen ich die Daten in einem 1-Dimensionalen Array abgelegt habe, das mit malloc erzeugt wurde. Das ist meiner Meinung nach einfacher zu handeln als sich mit mehrdimensionalen Arrays in C rumzuaergern, eben weil selbige nicht fortlaufend im Speicher liegen.
Code:
int *bombenfeld;
bombenfeld = malloc(sizeof(int) * zeilen * spalten);
Hiermit erzeugen wir dynamisch auf dem Heap Speicher (den wir bei Programmende mit free wieder freigeben sollten) -- es ist einfach ein grosser zusammenhaengender Speicherbereich dynamischer Groesse, in dem wir zeilen * spalten Integer-Werte ablegen koennen. Ok, in deinem Beispiel koenntest du wahrscheinlich sogar auf malloc verzichten, indem du das 1D-Array dynamisch auf dem Stack anlegst, aehnlich wie du es bereits mit deinem 2D-Array machst.
Wie greifst du auf die einzelnen Elemente in diesem Speicherbereich zu?
Code:
bombenfeld[spalten * j + i]
Denk drueber nach. Ist eigentlich recht simpel. D.h. statt ein mehrdimensionales Array hast du ein ganz normales einfaches Array, und die jeweilige Zelle berechnest du dir einfach mit spalten * j + i.
Nur wenn du diesen Loesungsansatz waehlst, musst du wie gesagt alle Stellen im Code umschreiben wo auf dieses Array zugegriffen wird, weil dein Ansatz mit mehrdimensionalen Arrays kann damit nicht funktionieren. Alternative 3 waere, du machst eine Pointer auf Pointer Struktur ala
int **bombenfeld, aehnlich wie argv -- dann koenntest du so auf das Array zugreifen wie bisher -- aber das ist von der Speicherverwaltung her etwas schwieriger als mein Loesungsansatz mit dem 1D-Array...
---------- Beitrag um 10:15 Uhr hinzugefügt ---------- Vorheriger Beitrag um 09:51 Uhr ----------
Selbst das Speichern von diversen Daten ist viel einfacher als gedacht, denn jede App hat zwei Verzeichnisse. Einmal für persistente und einmal für temporäre Dateien, die auch mal verloren gehen dürfen. So kann man mega leicht Sachen speichern.
Btw. iOS hat das gleiche Prinzip, jede App hat ein Cache-Verzeichnis das vom Betriebssystem geloescht werden kann, wenn der Speicher eng wird, und es gibt ein Verzeichnis wo man Daten speichert die wichtig sind und auch im Backup landen.