Unterschied zwischen Array-Typ und mit malloc zugewiesenem Array

Lesezeit: 5 Minuten

Benutzeravatar von Jorge Leitao
Jorge Leitao

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

Wir hatten auch eine ac-Datei mit einem sehr einfachen “readfile”. Die erste Zeile wurde gelesen ndie Anzahl der Zeilen, dann gab es eine Initialisierung von:

int list[n]

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

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

Schließlich haben wir die Listeninitialisierung auf geändert

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

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

Kann jemand erklären, warum dies aufgetreten ist? was verursachte den segfault mit int list[n]das wurde gestoppt, als wir mit der Verwendung begannen list = malloc(n*sizeof(int))?

  • Genau das, wonach ich gesucht habe, ich hatte das gleiche Problem bei der Manipulation von Harckerank-Arrays.

    – Kapil

    9. Oktober 2020 um 1:46 Uhr

Hier sind verschiedene Stücke im Spiel.

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 Aufbewahrungsdauer. Das bedeutet, dass das Array nur so lange lebt, wie die Funktion, die es aufruft, existiert. In der zweiten Version erhalten Sie Speicher mit dynamischer Speicherdauer, was bedeutet, dass er so lange existiert, bis er explizit freigegeben wird free.

Der Grund, warum die zweite Version hier funktioniert, ist ein Implementierungsdetail, wie C normalerweise kompiliert wird. Typischerweise ist der C-Speicher in mehrere Bereiche aufgeteilt, darunter der Stack (für Funktionsaufrufe und lokale Variablen) und der Heap (für mallocbearbeitete Objekte). Der Stack hat typischerweise eine viel kleinere Größe als der Heap; normalerweise sind es so etwas wie 8 MB. Wenn Sie also versuchen, ein riesiges Array mit zuzuweisen

int array[n];

Dann könnten Sie den Speicherplatz des Stacks überschreiten und den Segfault verursachen. Andererseits hat der Heap normalerweise eine enorme Größe (sagen wir, so viel Speicherplatz wie auf dem System frei ist) und so weiter mallocDas Speichern eines großen Objekts führt nicht zu einem Speichermangelfehler.

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

Hoffe das hilft!

  • Gute Antwort! Ich wollte fragen, ob es da auch Unterschiede in der Geschwindigkeit gibt?

    – Tropilio

    25. Dezember 2017 um 1:07 Uhr

  • Aufgrund der Auswirkungen der Referenzlokalität würde ich vermuten, dass auf das Stack-zugewiesene Array schneller zugegriffen werden kann, und malloc selbst ist viel langsamer als nur einen Stapelzeiger zu stoßen. Aber wirklich, es ist am besten, den Ansatz zu verwenden, der für die jeweilige Aufgabe besser geeignet ist.

    – Vorlagentypdef

    25. Dezember 2017 um 3:03 Uhr

  • Oder, wenn Sie int arr deklarieren[1000000]; außerhalb von Funktionen werden sie automatisch auf Null gesetzt und auf dem Heap gespeichert.

    – DSOI__UNUNOCTIUM

    7. Februar 2020 um 22:19 Uhr

  • @DSOI__UNUNOCTIUM Diese Arrays hätten eine statische Speicherdauer. Bist du sicher, dass sie auf dem Haufen gespeichert werden?

    – Vorlagentypdef

    7. Februar 2020 um 22:25 Uhr

  • Ich habe zuvor Arrays mit Größen von bis zu ein paar Hunderttausend zugewiesen. Ich werde es gleich mal testen.

    – DSOI__UNUNOCTIUM

    9. Februar 2020 um 4:18 Uhr

int list[n]

Weist Platz zu für n Ganzzahlen an der Stapel, die normalerweise ziemlich klein ist. Die Verwendung von Speicher auf dem Stack ist viel schneller als die Alternative, aber er ist ziemlich klein und es ist leicht, den Stack zum Überlaufen zu bringen (dh zu viel Speicher zuzuweisen), wenn Sie beispielsweise riesige Arrays zuweisen oder zu tief rekursieren. Sie müssen den auf diese Weise zugewiesenen Speicher nicht manuell freigeben, dies wird vom Compiler durchgeführt, wenn das Array den Gültigkeitsbereich verlässt.

malloc andererseits weist Platz in der zu Haufenwas normalerweise ist sehr groß im Vergleich zum Stack. Sie müssen dem Heap viel mehr Speicher zuweisen, um ihn zu erschöpfen, aber es ist viel langsamer, Speicher auf dem Heap zuzuweisen als auf dem Stack, und Sie müssen ihn manuell über freigeben free wenn Sie damit fertig sind.

  • “Die Verwendung von Speicher auf dem Stapel ist viel schneller als die Alternative”, meinen Sie hier “Zuweisung” oder “Zugriff”? AFAIK, die Stapelzuordnung ist viel schneller, aber gilt dies auch für den Zugriff (Lesen / Schreiben)? Vielen Dank

    – dragonxlwang

    5. August 2015 um 18:58 Uhr

int-Liste[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 viel größer ist.

int list[n] ist ein VLA, das auf dem Stack statt auf dem Heap alloziert. 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 zuweisen.

Benutzeravatar von thumbmunkeys
Daumenmücken

Diese Deklaration weist Speicher auf dem Stack zu

    int list[n]

malloc weist auf dem Heap zu.

Die Stack-Größe ist normalerweise kleiner als der Heap. Wenn Sie also zu viel Speicher auf dem Stack zuweisen, erhalten Sie einen Stackoverflow.

Siehe auch diese Antwort für weitere Informationen

Benutzeravatar von Flexo
Flexo

Angenommen, Sie haben eine typische Implementierung in Ihrer Implementierung, ist es höchstwahrscheinlich, dass:

int list[n]

zugewiesene Liste auf Ihrem Stack, wobei Folgendes gilt:

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

zugewiesenen Speicher auf Ihrem Heap.

Im Falle eines Stacks gibt es normalerweise eine Grenze dafür, wie groß diese werden können (wenn sie überhaupt wachsen können). Im Fall eines Heaps gibt es immer noch eine Grenze, die jedoch in der Regel viel stärker und (weitgehend) durch Ihren RAM + Swap + Adressraum eingeschränkt wird, der normalerweise mindestens eine Größenordnung größer ist, wenn nicht mehr.

Benutzeravatar von Manik Sidana
Manik Sidana

Wenn Sie unter Linux arbeiten, können Sie ulimit -s auf einen größeren Wert setzen, was möglicherweise auch für die Stack-Zuweisung funktioniert. Wenn Sie Speicher auf dem Stapel zuweisen, bleibt dieser Speicher bis zum Ende der Ausführung Ihrer Funktion erhalten. Wenn Sie Speicher auf dem Heap zuweisen (mit malloc), 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.

1417240cookie-checkUnterschied zwischen Array-Typ und mit malloc zugewiesenem Array

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy