it-swarm.com.de

Unterschied zwischen Array-Typ und Array, das mit Malloc zugewiesen wurde

Heute habe ich einem Freund von mir mit C-Code geholfen, und ich habe ein seltsames Verhalten gefunden, das ich ihm nicht erklären konnte, warum es passiert. Wir hatten eine TSV-Datei mit einer Liste von ganzen Zahlen mit einem int in jeder Zeile. Die erste Zeile war die Anzahl der Zeilen, die die Liste hatte.

Wir hatten auch eine c-Datei mit einer sehr einfachen "readfile". Die erste Zeile wurde auf n, die Anzahl der Zeilen, gelesen. Anschließend erfolgte die Initialisierung von:

int list[n]

und schließlich eine for-Schleife von n mit einem fscanf.

Für kleine n (bis ~ 100.000) war alles in Ordnung. Wir haben jedoch herausgefunden, dass ein Segfault auftritt, wenn n groß ist (10 ^ 6).

Schließlich haben wir die Listeninitialisierung in geändert

int *list = malloc(n*sizeof(int))

und alles wenn gut, auch bei sehr großen n.

Kann jemand erklären, warum dies passiert ist? was hat den segfault mit int list [n] verursacht, der angehalten wurde, wenn wir mit list = malloc (n * sizeof (int)) beginnen?

58
Jorge Leitão

Hier gibt es verschiedene Stücke.

Der erste ist der Unterschied zwischen der Deklaration eines Arrays als

int array[n];

und

int* array = malloc(n * sizeof(int));

In der ersten Version deklarieren Sie ein Objekt mit automatischer Speicherdauer. Dies bedeutet, dass das Array nur so lange lebt, wie die aufrufende Funktion vorhanden ist. In der zweiten Version erhalten Sie Speicher mit dynamischer Speicherdauer, was bedeutet, dass es solange existiert, bis es explizit mit free freigegeben wird.

Der Grund, dass die zweite Version hier funktioniert, ist ein Implementierungsdetail, wie C normalerweise kompiliert wird. Normalerweise ist der C-Speicher in mehrere Regionen aufgeteilt, einschließlich des Stapels (für Funktionsaufrufe und lokale Variablen) und des Heapspeichers (für malloced-Objekte). Der Stapel hat normalerweise eine viel kleinere Größe als der Haufen. Normalerweise ist es so etwas wie 8 MB. Als Ergebnis, wenn Sie versuchen, ein großes Array mit zuzuordnen

int array[n];

Dann könnten Sie den Speicherplatz des Stapels überschreiten und den Segfault verursachen. Auf der anderen Seite hat der Heap normalerweise eine riesige Größe (zum Beispiel so viel Speicherplatz, wie auf dem System frei ist), sodass mallocing ein großes Objekt keinen Out-of-Memory-Fehler verursacht.

Seien Sie im Allgemeinen vorsichtig mit Arrays mit variabler Länge in C. Sie können die Stapelgröße leicht überschreiten. Bevorzugen Sie malloc, es sei denn, Sie wissen, dass die Größe klein ist oder dass Sie das Array wirklich nur für kurze Zeit wünschen.

Hoffe das hilft! 

133
templatetypedef
int list[n]

Ordnet Platz für n-Ganzzahlen auf dem Stack zu, der normalerweise ziemlich klein ist. Die Verwendung von Speicher auf dem Stack ist viel schneller als die Alternative, aber er ist recht klein und es ist leicht, den Stack zu überlaufen (d. H. Zu viel Speicherplatz zuzuordnen), wenn Sie beispielsweise große Arrays zuordnen oder zu tief rekursieren. Sie müssen den auf diese Weise zugewiesenen Speicher nicht manuell freigeben. Dies wird vom Compiler ausgeführt, wenn das Array den Gültigkeitsbereich verlässt.

malloc weist dagegen Platz im heap zu, der normalerweise sehr groß ist, verglichen mit dem Stack. Sie müssen dem Heap eine viel größere Menge an Speicher zuweisen, um ihn zu erschöpfen, aber es ist viel langsamer, Speicher als dem Stack zuzuordnen, und Sie müssen ihn manuell über free freigeben, wenn Sie die Verwendung beendet haben es.

8
Seth Carnegie

int list [n] speichert die Daten im Stack, während Malloc sie im Heap speichert.

Der Stapel ist begrenzt und es gibt nicht viel Platz, während der Haufen viel größer ist.

2
Asier Gutierrez

int list[n] ist eine VLA, die auf dem Stack statt auf dem Heap reserviert. Sie müssen es nicht freigeben (es wird automatisch am Ende des Funktionsaufrufs freigegeben) und es wird schnell zugewiesen, aber der Speicherplatz ist sehr begrenzt, wie Sie festgestellt haben. Sie müssen dem Heap größere Werte zuordnen.

1
Puppy

Diese Deklaration weist Speicher auf dem Stapel zu

    int list[n]

malloc weist auf dem Heap zu.

Die Stackgröße ist normalerweise kleiner als der Heapspeicher. Wenn Sie also zu viel Speicherplatz auf dem Stack reservieren, erhalten Sie einen Stackoverflow. 

Siehe auch diese Antwort für weitere Informationen

1
thumbmunkeys

Angenommen, Sie haben eine typische Implementierung in Ihrer Implementierung, ist es sehr wahrscheinlich, dass

int list[n]

zugeordnete Liste auf Ihrem Stack, wobei:

int *list = malloc(n*sizeof(int))

zugewiesener Speicher auf Ihrem Heap. 

Im Falle eines Stapels gibt es normalerweise eine Begrenzung, wie groß diese wachsen können (wenn sie überhaupt wachsen können). Im Fall eines Heaps gibt es immer noch eine Grenze, die jedoch durch Ihren RAM + Swap + -Adressraum, der in der Regel mindestens um eine Größenordnung größer ist, wenn auch nicht mehr, tendenziell stark eingeschränkt wird.

1
Flexo
   int array[n];

Dies ist ein Beispiel für ein statisch zugewiesenes Array. Zum Zeitpunkt des Kompilierens ist die Größe des Arrays bekannt. Das Array wird auf dem Stack zugewiesen.

   int *array(malloc(sizeof(int)*n);

Dies ist ein Beispiel eines dynamisch zugewiesenen Arrays, und die Größe des Arrays wird dem Benutzer zur Laufzeit bekannt sein. Das Array wird auf dem Heap zugewiesen.

0
cammando

Wenn Sie Linux verwenden, können Sie ulimit -s auf einen höheren Wert setzen. Dies funktioniert möglicherweise auch für die Stapelzuweisung. Wenn Sie Speicher für den Stapel zuweisen, bleibt dieser Speicher bis zum Ende der Funktionsausführung erhalten. Wenn Sie Speicher für Heap (mit Malloc) zuweisen, können Sie den Speicher jederzeit freigeben (sogar vor dem Ende der Ausführung Ihrer Funktion).

Im Allgemeinen sollte Heap für große Speicherzuweisungen verwendet werden.

0
Manik Sidana

Wenn Sie eine malloc-Zuweisung verwenden, wird der Arbeitsspeicher vom Heap-Speicher und nicht vom Stack zugewiesen.

0
Tibor