Unterschied zwischen Array-Typ und mit malloc zugewiesenem Array
Lesezeit: 5 Minuten
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.
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
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.
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.
14172400cookie-checkUnterschied zwischen Array-Typ und mit malloc zugewiesenem Arrayyes
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