Segmentierungsfehler bei großen Arrays

Lesezeit: 7 Minuten

Segmentierungsfehler bei grosen Arrays
Mayank

Der folgende Code gibt mir einen Segmentierungsfehler, wenn er auf einem 2-GB-Computer ausgeführt wird, funktioniert aber 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?

Segmentierungsfehler bei grosen Arrays
Karl Salvia

Wahrscheinlich bekommen Sie hier nur einen Stapelüberlauf. Das Array ist zu groß, um in den Stapelbereich Ihres Programms zu passen; Die Stack-Wachstumsgrenze beträgt normalerweise 8 MiB oder 1 MiB für User-Space-Code auf den meisten Mainstream-Desktop-/Server-Betriebssystemen. (Normale C++-Implementierungen verwenden den asm-Stack für die automatische Speicherung, dh nicht-static lokale Variablen-Arrays. Dadurch wird die Zuweisung kostenlos aufgehoben, wenn Funktionen zurückkehren oder eine Ausnahme durch sie weitergegeben wird.)

Wenn Sie das Array dynamisch zuweisen, sollten Sie in Ordnung sein, vorausgesetzt, Ihr Computer verfügt über genügend Speicher.

int* array = new int[1000000];    // may throw std::bad_alloc

Aber denken Sie daran, dass dies von Ihnen verlangt wird delete[] das Array manuell, um Speicherlecks zu vermeiden, selbst wenn Ihre Funktion über eine Ausnahme beendet wird. Von manuellem Neuen/Löschen wird in modernem C++ dringend abgeraten, lieber RAII.


Eine bessere Lösung wäre zu verwenden std::vector<int> array (cpReferenz). Sie können Platz für 1000000 Elemente reservieren, wenn Sie wissen, wie groß es werden wird. Oder auch resize es, sie standardmäßig zu konstruieren (dh den Speicher mit Null zu initialisieren, anders als wenn Sie ein einfaches Array im C-Stil ohne Initialisierer deklarieren), wie std::vector<int> array(1000000)

Wenn das std::vector Wenn ein Objekt den Gültigkeitsbereich verlässt, wird sein Destruktor den Speicher für Sie freigeben, selbst wenn dies über eine Ausnahme in einer untergeordneten Funktion geschieht, die von einer übergeordneten Funktion abgefangen wird.

  • Danke für die Antwort, aber könnten Sie mir erklären, warum Arrays auf dem Stapel zugewiesen werden und warum nicht im Hauptprogrammspeicher.

    – Mayank

    4. Dezember 2009 um 15:51 Uhr

  • Der angegebene Code wird auf dem Stapel zugewiesen, da er zur Kompilierzeit als Array mit einer konstanten Anzahl von Elementen angegeben wird. Werte werden nur mit malloc, new usw. auf den Heap gelegt.

    – Seth Johnson

    4. Dezember 2009 um 16:05 Uhr

  • Alle automatischen Variablen werden auf dem Stack allokiert. Wenn Sie sich das 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 nach Speicherblöcken, die groß genug sind, um Ihre Anforderung zu erfüllen.

    – wiederholen

    4. Dezember 2009 um 16:12 Uhr

  • @Charles, warum könnten wir mehr Speicher vom Heap zuweisen, nicht vom Stack? Nach meinem Verständnis bewegen sich sowohl Stack als auch Heap im zugewiesenen Adressraum im Speicher in entgegengesetzte Richtung.

    – Saurabh-Agarwal

    24. Februar 2015 um 6:58 Uhr

  • @saurabhagarwal Der Haufen bewegt sich nicht. Es ist nicht einmal eine zusammenhängende Speicherregion. Der Zuordner gibt einfach einen freien Speicherblock zurück, der Ihrer Größenanforderung entspricht. Was und wo sind Stack und Heap?

    – phuklv

    23. Juni 2015 um 4:45 Uhr

1647282613 206 Segmentierungsfehler bei grosen Arrays
Günther Piez

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

Weisen Sie es nicht lokal auf dem Stack zu, sondern verwenden Sie stattdessen einen anderen Ort. Dies kann erreicht werden, indem entweder das Objekt hergestellt wird global oder es global zuordnen Haufen. Globale Variablen sind in Ordnung, wenn Sie die von keiner anderen Kompilationseinheit verwenden. Um sicherzustellen, dass dies nicht versehentlich passiert, fügen Sie einen statischen Speicherbezeichner hinzu, andernfalls verwenden Sie einfach den Heap.

Dadurch wird das BSS-Segment zugewiesen, das Teil des Heaps ist. Da es sich im statischen Speicher befindet, wird es mit Null initialisiert, wenn Sie nichts anderes angeben, im Gegensatz zu lokalen Variablen (automatischer Speicher) einschließlich Arrays.

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

Ein Nicht-Null-Initialisierer bewirkt, dass ein Compiler im DATA-Segment zuweist, das ebenfalls Teil des Heaps ist. (Und alle Daten für den Array-Initialisierer nehmen Platz in der ausführbaren Datei ein, einschließlich aller impliziten nachgestellten Nullen, anstatt nur eine Größe für Zero-Init im BSS)

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

Dadurch wird an einer nicht angegebenen Stelle im Heap zugewiesen:

int main()
{
   int* c = new int[1000000];  // size can be a variable, unlike with static storage
   cout << "done\n";
   delete[] c;            // dynamic storage needs manual freeing
   return 0;
}

  • Wenn Sie das dritte Muster verwenden und auf dem Heap zuweisen, vergessen Sie nicht, zu löschen[] den Zeiger irgendwann, oder Sie werden Speicher verlieren. Oder schauen Sie sich intelligente Hinweise an.

    – davidA

    5. September 2012 um 1:20 Uhr

  • @meowsqueak Natürlich ist es eine gute Übung delete überall, wo Sie mit zuweisen new. Aber wenn Sie sicher sind, dass Sie Speicher nur einmal zuweisen (wie in main), ist dies streng genommen nicht erforderlich – der Speicher wird garantiert beim Beenden von main freigegeben, auch ohne explizit delete.

    – Günther Piez

    5. September 2012 um 8:11 Uhr

  • ‘at’drhirsch (wie macht man überhaupt ein at-Zeichen?) – ja, fairer Kommentar. Da das OP neu in der Sprache 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

    5. September 2012 um 12:19 Uhr


1647282613 354 Segmentierungsfehler bei grosen Arrays
RSFalcon7

Wenn Sie die meisten UNIX- und Linux-Systeme verwenden, können Sie die Stack-Größe mit dem folgenden Befehl vorübergehend erhöhen:

ulimit -s unlimited

Aber sei vorsichtig, das Gedächtnis ist eine begrenzte Ressource und mit großer Macht kommen große Verantwortungen 🙂

  • Dies ist die Lösung, aber ich rate allen, beim Entfernen dieser Standardbeschränkungen für die Stapelgröße des Programms äußerst vorsichtig zu sein. Sie werden nicht nur einen starken Leistungsabfall erleben, sondern Ihr System kann abstürzen. Zum Beispiel habe ich versucht, ein Array mit 16 000 000 Integer-Elementen mit Quicksort auf einer Maschine mit 4 GB RAM zu sortieren, und mein System wurde fast zerstört. lol

    – rbaleksandar

    16. Oktober 2014 um 16:51 Uhr

  • @rbaleksandar Ich denke, Sie ~ 16 MB 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

    16. Oktober 2014 um 23:20 Uhr

  • 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 scheiße ist, Bubblesort ist etwas, das Sie unmöglich falsch implementieren können. lol

    – rbaleksandar

    17. Oktober 2014 um 10:07 Uhr

  • LOL, du könntest Radix Sort ausprobieren oder einfach std::sort verwenden 🙂

    – RSFalcon7

    17. Oktober 2014 um 16:54 Uhr

  • Keine Chance. Es ist eine Laboraufgabe. 😀

    – rbaleksandar

    17. Oktober 2014 um 17:00 Uhr

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

Segmentierungsfehler bei grosen Arrays
Narek

Weil Sie das Array im Stack speichern. Sie sollten es auf dem Haufen lagern. Sehen dieser Link das Konzept des Haufens und des Stacks zu verstehen.

1647282614 500 Segmentierungsfehler bei grosen Arrays
Arti

Ihr einfaches Array wird im Stapel zugewiesen, und der Stapel ist auf wenige Magabyte begrenzt, daher bekommt Ihr Programm einen Stapelüberlauf und stürzt ab.

Wahrscheinlich ist es am besten, heap-allocated zu verwenden std::Vektor-basiertes Array, das anstelle Ihres einfachen Arrays fast auf die Größe des gesamten Speichers wachsen kann.

Probieren Sie es online aus!

#include <vector>
#include <iostream>

int main() {
   std::vector<int> c(1000000);
   std::cout << "done\n";
   return 0;
}

Dann können Sie wie gewohnt auf die Elemente des Arrays zugreifen c[i] und/oder erhalten Sie seine Größe c.size() (Anzahl von int Elemente).

Wenn Sie ein mehrdimensionales Array mit festen Dimensionen wünschen, verwenden Sie eine Mischung aus beiden std::Vektor und std::arraywie folgt:

Probieren Sie es online aus!

#include <vector>
#include <array>
#include <iostream>

int main() {
   std::vector<std::array<std::array<int, 123>, 456>> c(100);
   std::cout << "done\n";
   return 0;
}

Im obigen Beispiel erhalten Sie fast das gleiche Verhalten, als ob Sie ein einfaches Array zugewiesen hätten int c[100][456][123]; (mit der Ausnahme, dass der Vektor auf dem Heap statt auf dem Stack zuweist), können Sie auf Elemente zugreifen als c[10][20][30] wie im einfachen Array. Dieses obige Beispiel weist auch Arrays auf dem Heap zu, was bedeutet, dass Sie Arraygrößen bis zur gesamten Speichergröße haben können und nicht durch die Stapelgröße begrenzt sind.

Um den Zeiger auf das erste Element im Vektor zu erhalten, verwenden Sie &c[0] oder nur c.data().

Es kann noch einen Weg geben, der für mich funktioniert hat! Sie können die Größe des Arrays reduzieren, indem Sie seinen Datentyp ändern:

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

oder

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

1002720cookie-checkSegmentierungsfehler bei großen Arrays

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

Privacy policy