Speicher reservieren für sehr großes Array (C)?

7 Antworten

Vom Beitragsersteller als hilfreich ausgezeichnet
Neben Stack und Heap gibt es auch noch die Bss- und Data-Segmente (und weitere, die hier keine Rolle spielen)!

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ß! :)

iVeniiX 
Beitragsersteller
 03.12.2017, 12:56

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!

0

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.


TeeTier  03.12.2017, 10:14

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! :)

0
PWolff  03.12.2017, 23:11
@TeeTier

1 MiByte? Verwöhnter Jungspund! Zu meiner Zeit kostete ein 1-KiB-RAM-IC ca. 10 DM, und der C64, der später rauskam und tatsächlich 64 KiB RAM adressieren konnte, war ein technisches Wunderwerk.

0

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

Woher ich das weiß:Berufserfahrung – Software-Entwickler

TeeTier  03.12.2017, 10:49

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! ;)

0

statischer speicher sollte kein problem sein , das sind ja gerade mal 9 MB , dein Problem muss woanders liegen .

Das limit liegt bei 2GByte


iVeniiX 
Beitragsersteller
 02.12.2017, 15:07

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

0
RakonDark  02.12.2017, 17:15
@iVeniiX

dann würde ich das ganze mal auf pastebin kopieren und hier den link reinsetzen . scheint wohl irgendwas zu sein .

0