2012-05-13 5 views
48

Heute half ich einem Freund von mir mit einigen C-Code, und ich habe ein seltsames Verhalten gefunden, dass ich ihm nicht erklären konnte, warum es geschah. Wir hatten TSV-Datei mit einer Liste von ganzen Zahlen, mit einem int jeder Zeile. Die erste Zeile war die Anzahl der Zeilen, die die Liste hatte.Unterschied zwischen Array-Typ und Array mit Malloc zugeordnet

Wir hatten auch eine c-Datei mit einer sehr einfachen "readfile". Die erste Zeile n gelesen wurde, die Anzahl der Zeilen, dann gab es eine Initialisierung von:

int list[n] 

und schließlich ein für die Schleife von n mit einem fscanf.

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

Schließlich haben wir die Liste der Initialisierung

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

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

Kann jemand erklären, warum das passiert ist? Was verursachte den segfault mit int list [n], der gestoppt wurde, als wir list = malloc (n * sizeof (int)) verwendeten?

Antwort

109

Hier sind mehrere verschiedene Stücke im Spiel.

Die erste ist der Unterschied ein Array als

int array[n]; 

und

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

In der ersten Version zwischen deklariert, sind Sie ein Objekt mit automatischer Speicherdauer zu deklarieren. Dies bedeutet, dass das Array nur so lange lebt, wie die Funktion existiert, die es aufruft. In der zweiten Version erhalten Sie Speicher mit dynamischer Speicherdauer, was bedeutet, dass er so lange existiert, bis er explizit mit free freigegeben wird.

Der Grund, dass die zweite Version hier funktioniert, ist ein Implementierungsdetail von, wie C normalerweise kompiliert wird. In der Regel wird der C-Speicher in mehrere Bereiche aufgeteilt, darunter der Stapel (für Funktionsaufrufe und lokale Variablen) und der Heap (für malloc Ed-Objekte). Der Stapel hat typischerweise eine viel kleinere Größe als der Stapel; normalerweise ist es ungefähr 8MB. Als Ergebnis, versuchen Sie, wenn Sie mit

int array[n]; 

eine riesige Auswahl zuweisen Dann könnten Sie den Stapel Speicherplatz überschreiten, die segfault verursacht. Auf der anderen Seite hat der Heap normalerweise eine riesige Größe (z. B. so viel Speicherplatz, wie auf dem System frei ist), und daher verursacht ein großes Objekt keinen Fehler wegen zu wenig Arbeitsspeicher.

Im Allgemeinen sollten Sie bei Arrays mit variabler Länge in C vorsichtig sein. 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 nur für eine kurze Zeit wirklich wollen.

Hoffe, das hilft!

+1

Sehr aufschlussreiche Antwort ... danke! –

+1

Große Antwort! Ich frage mich, ob es auch einen Geschwindigkeitsunterschied gibt? –

+1

Aufgrund der Auswirkungen der Lokalität der Referenz würde ich vermuten, dass das Array mit dem zugewiesenen Stack schneller zugänglich ist, und "Malloc" selbst ist viel langsamer als nur einen Stack-Pointer zu stoßen. Aber am besten ist es, den Ansatz zu verwenden, der für die jeweilige Aufgabe geeigneter ist. – templatetypedef

2

int list [n] speichert die Daten im Stapel, 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.

1

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

1

Diese Erklärung weist Speicher auf dem Stapel

int list[n] 

malloc auf dem Heap reserviert.

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

Siehe auch this answer for further information

0

Wenn Sie ein malloc zurechnen verwenden, Speicher von Heap zugewiesen wird und nicht vom Stapel, die viel begrenzte Größe ist.

8
int list[n] 

Ordnet Raum für n ganze Zahlen auf den Stapel, die in der Regel ziemlich klein ist. Das Verwenden von Speicher auf dem Stapel ist viel schneller als die Alternative, aber es ist ziemlich klein und es ist leicht, den Stapel zu überfließen (d. H. Zu viel Speicher zuzuweisen), wenn Sie z. B. große Arrays zuweisen oder Rekursion zu tief durchführen. 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 auf der anderen Seite teilt Platz im Haufen, die in der Regel sehr großen im Vergleich zu dem Stapel.Sie müssen viel Speicherplatz auf dem Heap reservieren, um es auszuschöpfen, aber es ist viel langsamer, Speicher auf dem Heapspeicher zuzuordnen als auf dem Stack, und Sie müssen ihn manuell über free freigeben, wenn Sie fertig sind es.

+1

"Die Verwendung von Speicher auf dem Stapel ist viel schneller als die Alternative", meinst du mit "Zuweisung" oder "Zugriff"? AFAIK, Stapelzuordnung ist viel schneller, aber gilt es auch für den Zugriff (Lesen/Schreiben)? Danke – dragonxlwang

1

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

int list[n] 

zugeordnet Liste auf Ihrem Stapel, wo, wie:

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

zugewiesenen Speicher auf dem 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 ein Limit, aber das ist tendenziell sehr groß und (im Wesentlichen) eingeschränkt durch Ihren RAM + Swap + Adressraum, der typischerweise mindestens eine Größenordnung größer, wenn nicht sogar mehr ist.

0

Wenn Sie Linux verwenden, können Sie ulimit -s auf einen höheren Wert setzen, und dies könnte auch für die Stapelzuweisung funktionieren. Wenn Sie Speicher auf Stapel zuweisen, bleibt dieser Speicher bis zum Ende der Ausführung Ihrer Funktion. 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.

0
int array[n]; 

Es ist ein Beispiel für statisch zugewiesene Array und zum Zeitpunkt der Kompilierung wird die Größe des Arrays bekannt sein. Und das Array wird auf dem Stapel zugewiesen.

Es ist ein Beispiel für dynamisch zugewiesene Array und die Größe des Arrays wird dem Benutzer zur Laufzeit bekannt sein. Und das Array wird auf dem Heap zugewiesen.

Verwandte Themen