Segmentierungsfehler bei großen Arraygrößen

116

Der folgende Code gibt mir einen Segmentierungsfehler, wenn er auf einem 2-GB-Computer ausgeführt wird, funktioniert jedoch auf einem 4-GB-Computer.

int main()
{
   int c[1000000];
   cout << "done\n";
   return 0;
}

Die Größe des Arrays beträgt nur 4 MB. Gibt es eine Begrenzung für die Größe eines Arrays, das in c ++ verwendet werden kann?

Mayank
quelle

Antworten:

130

Sie bekommen hier wahrscheinlich nur einen Stapelüberlauf. Das Array ist zu groß, um in den Stapeladressraum Ihres Programms zu passen.

Wenn Sie das Array auf dem Heap zuweisen, sollte dies in Ordnung sein, vorausgesetzt, Ihr Computer verfügt über genügend Speicher.

int* array = new int[1000000];

Denken Sie jedoch daran, dass Sie hierfür delete[]das Array benötigen . Eine bessere Lösung wäre, es zu verwenden std::vector<int>und seine Größe auf 1000000 Elemente zu ändern.

Charles Salvia
quelle
3
Vielen Dank für die Antwort, aber können Sie mir erklären, warum Arrays auf dem Stapel zugewiesen sind und warum nicht im Hauptprogrammspeicher.
Mayank
18
Der angegebene Code wird dem Stapel zugewiesen, da er zur Kompilierungszeit als Array mit einer konstanten Anzahl von Elementen angegeben wird. Werte werden nur mit Malloc, Neuem usw. auf den Haufen gelegt
Seth Johnson
6
Alle automatischen Variablen werden auf dem Stapel zugeordnet. Wenn Sie sich die Disasseble ansehen, sehen Sie die Größe Ihrer lokalen Variablen, die vom Stapelzeiger abgezogen werden. Wenn Sie malloc oder calloc oder eine der Speicherfunktionen aufrufen, suchen die Funktionen Speicherblöcke, die groß genug sind, um Ihre Anforderung zu erfüllen.
Wiederholen Sie den
@ Charles, warum könnten wir mehr Speicher vom Heap zuweisen, nicht vom Stapel? Nach meinem Verständnis bewegen sich sowohl Stapel als auch Heap im zugewiesenen Adressraum im Speicher in die entgegengesetzte Richtung.
Saurabh Agarwal
2
@saurabhagarwal Der Haufen bewegt sich nicht. Es ist nicht einmal eine zusammenhängende Speicherregion. Der Allokator gibt einfach einen freien Speicherblock zurück, der Ihren Größenanforderungen entspricht. Was und wo befinden sich der Stapel und der Heap?
Phuclv
56

In C oder C ++ werden lokale Objekte normalerweise auf dem Stapel zugewiesen. Sie weisen dem Stapel ein großes Array zu, das mehr als der Stapel verarbeiten kann, sodass Sie einen Stapelüberlauf erhalten.

Ordnen Sie es nicht lokal auf dem Stapel zu, sondern verwenden Sie stattdessen einen anderen Ort. Dies kann erreicht werden, indem das Objekt entweder global gemacht oder dem globalen Heap zugewiesen wird . Globale Variablen sind in Ordnung, wenn Sie die von keiner anderen Kompilierungseinheit verwenden. Um sicherzustellen, dass dies nicht versehentlich geschieht, fügen Sie einen statischen Speicherspezifizierer hinzu, andernfalls verwenden Sie einfach den Heap.

Dies wird im BSS-Segment zugeordnet, das Teil des Heaps ist:

static int c[1000000];
int main()
{
   cout << "done\n";
   return 0;
}

Dies wird im DATA-Segment zugeordnet, das ebenfalls Teil des Heaps ist:

int c[1000000] = {};
int main()
{
   cout << "done\n";
   return 0;
}

Dies wird an einer nicht angegebenen Stelle im Heap zugewiesen:

int main()
{
   int* c = new int[1000000];
   cout << "done\n";
   return 0;
}
Gunther Piez
quelle
Wenn Sie das dritte Muster verwenden, das auf dem Heap zugeordnet ist, vergessen Sie nicht, den Zeiger irgendwann zu löschen [], da sonst Speicherplatz verloren geht. Oder schauen Sie sich intelligente Zeiger an.
DavidA
8
@meowsqueak Natürlich ist es eine gute Praxis, deleteüberall dort , wo Sie zuweisen new. Wenn Sie jedoch sicher sind, dass Sie den Speicher nur einmal zuweisen (wie in main), wird er strikt nicht benötigt - der Speicher wird am Ausgang von main garantiert auch ohne explizite Freigabe freigegeben delete.
Gunther Piez
'at'drhirsch (wie macht man überhaupt einen at-Charakter?) - ja, fairer Kommentar. Da das OP für die Sprache neu erscheint, wollte ich nur sicherstellen, dass sie und alle anderen, die Ihre gute Antwort sehen, sich der Auswirkungen der dritten Option bewusst sind, wenn sie allgemein verwendet werden.
DavidA
15

Wenn Sie in den meisten UNIX- und Linux-Systemen ausgeführt werden, können Sie die Stapelgröße vorübergehend mit dem folgenden Befehl erhöhen:

ulimit -s unlimited

Aber seien Sie vorsichtig, Speicher ist eine begrenzte Ressource und mit großer Kraft gehen große Verantwortlichkeiten einher :)

RSFalcon7
quelle
1
Dies ist die Lösung, aber ich rate allen, äußerst vorsichtig zu sein, wenn diese Standardbeschränkungen für die Stapelgröße des Programms aufgehoben werden. Sie werden nicht nur einen starken Leistungsabfall feststellen, sondern Ihr System kann auch abstürzen. Zum Beispiel habe ich versucht, ein Array mit 16.000.000 ganzzahligen Elementen mit Quicksort auf einem Computer mit 4 GB RAM zu sortieren, und mein System wurde fast zerstört. LOL
rbaleksandar
@rbaleksandar Ich denke, Sie ~ 16MB Programm töten fast Ihren Computer, weil Sie mit mehreren Kopien des Arrays gearbeitet haben (möglicherweise eine pro Funktionsaufruf?) versuchen Sie eine speicherbewusstere Implementierung;)
RSFalcon7
Ich bin mir ziemlich sicher, dass die Array-Behandlung in Ordnung ist, da ich als Referenz und nicht als Wert übergebe. Das gleiche passiert mit Bubblesort. Zur Hölle, auch wenn meine Implementierung von Quicksort Bubblesort nervt, ist etwas, das Sie unmöglich falsch implementieren können. LOL
rbaleksandar
LOL Sie könnten Radix sort versuchen, oder einfach std :: sort verwenden :)
RSFalcon7
1
Keine Chance. Es ist eine Laboraufgabe. : D
rbaleksandar
3

In diesem Fall wird Ihr Array auf dem Stapel zugewiesen. Versuchen Sie, mithilfe von alloc ein Array derselben Größe zuzuweisen.

Wiederholung
quelle
3

Weil Sie das Array im Stapel speichern. Sie sollten es auf dem Haufen speichern. Unter diesem Link finden Sie Informationen zum Konzept des Heaps und des Stacks.

Narek
quelle