Speicher reservieren für sehr großes Array (C)?
Hallo!
Ich habe in C beispielsweise ein Array char arr[3000][3000] aber kann damit nichts machen weil mir beim kompilieren das Programm abstürzt, also dachte ich mir ich muss eventuell erst Speicher dafür reservieren mit malloc oder calloc oder etwas in der Art. Aber wie mache ich denn das bei so einem multidimensionalem Array? Und wie kann ich dann z.B. eingeben, dass auf jedem Feld in dem riesigen Array ein X steht? (Also ein riesiges Feld aus X'en)
LG V.
7 Antworten
Deshalb kannst du ein riiieeesiges, dickes, fettes Array auch "halb-statisch" in deiner Übersetzungseinheit (in diesem Falle der Quelltext-Datei) deklarieren und zum initialisieren memset() nutzen.
Falls du das nicht verstehst, keine Angst! Guck dir einfach folgendes Beispiel an:
#include <stdio.h> /* printf */
#include <stdlib.h> /* EXIT_SUCCESS */
#include <string.h> /* memset */
#define WDT 3000
#define HGT 3000
static char arr[HGT][WDT];
int main(void) {
memset(arr, 'X', HGT * WDT);
printf("Value test: '%c'\n", arr[123][456]);
return EXIT_SUCCESS;
}
Dabei wird das Kompilat auch nicht unnötig aufgeblasen, und die resultierende Datei ist - zumindest bei mir - rund 8KB groß ... also kein Unterschied zu "Hello World". :)
Das liegt daran, dass dein Array im Bss-Segment landet, und zur Laufzeit - noch vor main() - automatisch alloziert wird.Wenn du die Definition zu ...
static char arr[HGT][WDT] = { {'X'} };
... änderst, dann landet das Array im Datensegment, wobei das Array nicht mehr vor main() alloziert, sondern aus dem Kompilat direkt in den Speicher gemappt wird. Dementsprechend ist bei mir das ausführbare Programm auch nicht mehr nur 8 KB, sondern über 9 MB groß.
Aber da du sowieso das gesamte Array mithilfe von memset() mit 'X'en füllen willst, und nicht wie in der Zeile oben nur das erste Element, wäre diese Variante Blödsinn, und das Bss-Segment - so wie im Miniprogrämmchen weiter oben - vorzuziehen.
So große statische Arrays im Datensegment eignen sich eigentlich nur für vorberechnete Tabellen, deren Berechnung zur Laufzeit (oder bei C++ auch zur Kompilierzeit) zu aufwändig wäre.
Das "static" Schlüsselwort vor der Definition sorgt übrigens dafür, dass das Array nicht global in anderen Quelltextdateien sichtbar ist und somit u. a. Namenskonflikte vermieden werden können.
Variante 2 mit "echtem" dynamischem Speicher:Wenn du unbedingt malloc() verwenden willst, geht das z. B. so:
#include <stdio.h> /* printf */
#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS, size_t */
#include <string.h> /* memset */
#define WDT 3000
#define HGT 3000
int main(void) {
int result = EXIT_FAILURE;
const size_t size = HGT * WDT;
char * ptr = malloc(size);
char c = '\0';
if (ptr) {
memset(ptr, 'X', size);
c = *(ptr + 123 * WDT + 456);
printf("Value test: '%c'\n", c);
free(ptr);
result = EXIT_SUCCESS;
}
return result;
}
Beachte hierbei, dass es sich um einen Zeiger und nicht mehr um ein zwei dimensionales Array handelt. Dabei gibt es tatsächlich wichtige Unterschiede, aber die kannst du jetzt erst mal ignorieren (lernst du später irgendwann sowieso).
Der Speicherbereich wird erst initialisiert, nachdem sichergestellt wurde, dass die Speicherallokation erfolgreich verlief, d. h. der Zeiger nicht NULL ist. Am Ende wird der Speicher wieder frei gegeben (was man unbedingt IMMER tun sollte).
Dazwischen wird auf den Speicherbereich anders zugegriffen, als auf ein 2D-Array, aber wenn du es dir genau ansiehst, wirst du verstehen, wie es funktioniert. Wenn nicht, mach dir keinen Kopf! Das wirst du in den nächsten Wochen vermutlich sowieso lernen. :)
Auch bei dieser Version ist die kompilierte Programmdatei natürlich wieder nur knapp 8KB groß, da der Speicher ja in keinem Segment, sondern zur Laufzeit auf dem Heap reserviert wird.
So, und zum Schluss noch ein Hinweis: Du kannst dem Compiler auch sagen, dass er (bzw. die Programmdatei zur Laufzeit) besonders viel Speicher für den Stack reservieren soll. Damit ist es dann auch möglich, dein großes 2D-Array auf den Stack zu packen, ohne dass dein Programm abstürzt.
Allerdings ist diese Methode m. M. n. sehr unsauber, und deshalb schreibe ich jetzt nicht, wie das geht. Wer es dennoch wissen will, der sollte die Dokumentation seines Compilers konsultieren. :)
Fazit: Du kannst Speicher also auf dem Stack, auf dem Heap oder in verschiedenen Segmenten (hier Data und Bss) reservieren. All diese Varianten haben große Vor- und auch große Nachteile. Wann du welche Methode nutzen solltest, wirst du mit der Zeit automatisch lernen, also mach dir keine Gedanken, wenn das Ganze im Moment noch undurchsichtig wirkt.
Also dann ... viel Spaß! :)Oha, stark ausführlich! Allerdings dürfen wir nur malloc, calloc und free benutzen, keine anderen Bibliotheksfunktionen (Wir laden das dann hoch und der dortige Compiler fügt eine main Funktion und Bibos ein), deshalb nützt mir das wohl oder übel nicht viel. Trotzdem danke!
sich dir was aus von den lösungen
oder hier mal erklärt
https://www.eskimo.com/~scs/cclass/int/sx9b.html
dynamisch .
Durch das große Array gehst du über den Stack Bereich hinaus und überschreibst verschiedene Speicheradressen deines Programmes, bis es abstürzt.
Eine Lösung ist, wie du es schon selbst weiß, den Heap zu benutzen. Das erreichst du durch das benutzen von malloc, sowie calloc.
Lösungen dazu finden man zu hauf im Internet, einfach mal nach Stichpunkten wie "2D array malloc" googeln. Immerhin hatte Leute schon vor Jahrzehnten das gleiche Problem wie du.
Vermutlich hält der Stack das nicht aus; dann musst du das Array auf den Heap legen. Nimm also einen Zeiger auf das Array statt des Array selbst. (Direkte Arrays kenne ich aus C / C++ sowieso praktisch nicht, nur Zeiger darauf.)
Zum Füllen mit einem bestimmten Zeichen: https://www.google.de/search?q=c+fill+memory+with+byte
Genau genommen ist das laut Anhang J.2 des Standards undefiniertes Verhalten, aber jeder Compiler, den ich kenne, füllt mehrdimensionale char-Arrays problemlos mit memset().
Deshalb habe ich in meiner Antwort auch memset() genommen.
Ist aber genau genommen UB ... aber wir wollen mal nicht päpstlicher sein, als der Papst! ;)
statischer speicher sollte kein problem sein , das sind ja gerade mal 9 MB , dein Problem muss woanders liegen .
Das limit liegt bei 2GByte
Aber wo finde ich denn das Problem wenn mir das Programm einfach abschmiert? Wenn ich statt 3000x3000 nur zB 3x3 eingebe dann kompiliert er und gibt die Werte aus, aber bei sowas großem stürzt das ab
Mein erster Rechner hatte nicht mal 1MB RAM ... früher konnte man nur davon träumen, ein 9MB großes 2D-Array zu allozieren.
Die Jugend von heute ist einfach viel zu verwöhnt! Frechheit! :)