Ich habe eine Weile programmiert, aber es waren hauptsächlich Java und C #. Ich musste das Gedächtnis nie alleine verwalten. Ich habe vor kurzem angefangen, in C ++ zu programmieren, und bin ein wenig verwirrt darüber, wann ich Dinge auf dem Stapel speichern soll und wann ich sie auf dem Heap speichern soll.
Nach meinem Verständnis sollten Variablen, auf die sehr häufig zugegriffen wird, auf dem Stapel und den Objekten gespeichert werden, selten verwendete Variablen und große Datenstrukturen sollten alle auf dem Heap gespeichert werden. Ist das richtig oder bin ich falsch?
Antworten:
Nein, der Unterschied zwischen Stack und Heap ist nicht die Leistung. Es ist die Lebensdauer: Jede lokale Variable innerhalb einer Funktion (alles, was Sie nicht malloc () oder neu tun) befindet sich auf dem Stapel. Es verschwindet, wenn Sie von der Funktion zurückkehren. Wenn Sie möchten, dass etwas länger lebt als die Funktion, die es deklariert hat, müssen Sie es dem Heap zuweisen.
Um besser zu verstehen, was der Stapel ist, schauen Sie am anderen Ende nach - anstatt zu verstehen, was der Stapel in Bezug auf eine Hochsprache tut, schauen Sie nach "Aufrufstapel" und "Aufrufkonvention" und sehen Sie, was Die Maschine funktioniert wirklich, wenn Sie eine Funktion aufrufen. Der Computerspeicher besteht nur aus einer Reihe von Adressen. "Heap" und "Stack" sind Erfindungen des Compilers.
quelle
Ich würde sagen:
Speichern Sie es auf dem Stapel, wenn Sie können.
Speichern Sie es auf dem Haufen, wenn Sie müssen.
Ziehen Sie daher den Stapel dem Haufen vor. Einige mögliche Gründe, warum Sie etwas nicht auf dem Stapel speichern können, sind:
Mit sinnvollen Compilern ist es möglich, Objekte mit nicht fester Größe auf dem Heap zuzuweisen (normalerweise Arrays, deren Größe zur Kompilierungszeit nicht bekannt ist).
quelle
Es ist subtiler als die anderen Antworten vermuten lassen. Es gibt keine absolute Trennung zwischen Daten auf dem Stapel und Daten auf dem Heap, basierend darauf, wie Sie sie deklarieren. Beispielsweise:
Im Hauptteil einer Funktion wird ein
vector
(dynamisches Array) von zehn Ganzzahlen auf dem Stapel deklariert . Der von der verwaltete Speichervector
befindet sich jedoch nicht auf dem Stapel.Ah, aber (die anderen Antworten deuten darauf hin) die Lebensdauer dieses Speichers ist durch die Lebensdauer des Speichers selbst begrenzt
vector
, der hier stapelbasiert ist. Daher spielt es keine Rolle, wie er implementiert wird - wir können ihn nur als stapelbasiertes Objekt behandeln mit Wertesemantik.Nicht so. Angenommen, die Funktion war:
Alles mit einer
swap
Funktion (und jeder komplexe Werttyp sollte eine haben) kann als eine Art wiederbindbarer Verweis auf einige Heap-Daten unter einem System dienen, das einen einzelnen Eigentümer dieser Daten garantiert.Daher besteht der moderne C ++ - Ansatz darin, die Adresse von Heap-Daten niemals in nackten lokalen Zeigervariablen zu speichern. Alle Heap-Zuordnungen müssen in Klassen ausgeblendet sein.
Wenn Sie dies tun, können Sie sich alle Variablen in Ihrem Programm als einfache Werttypen vorstellen und den Heap insgesamt vergessen (außer beim Schreiben einer neuen wertähnlichen Wrapper-Klasse für einige Heap-Daten, was ungewöhnlich sein sollte). .
Sie müssen lediglich ein spezielles Wissen behalten, um die Optimierung zu unterstützen: Wenn möglich, anstatt eine Variable einer anderen wie folgt zuzuweisen:
Tauschen Sie sie so aus:
weil es viel schneller ist und keine Ausnahmen auslöst. Die einzige Voraussetzung ist, dass Sie nicht
b
weiterhin denselben Wert beibehalten müssen (a
stattdessen wird der Wert abgerufen, der in den Papierkorb verschoben wirda = b
).Der Nachteil ist, dass Sie bei diesem Ansatz gezwungen sind, Werte von Funktionen über Ausgabeparameter anstelle des tatsächlichen Rückgabewerts zurückzugeben. Aber sie beheben das in C ++ 0x mit rWert-Referenzen .
In den kompliziertesten Situationen von allen würden Sie diese Idee auf das äußerste Extrem bringen und eine intelligente
shared_ptr
Zeigerklasse verwenden, wie sie bereits in tr1 enthalten ist. (Obwohl ich behaupten würde, dass Sie, wenn Sie es zu brauchen scheinen, möglicherweise außerhalb des Sweetspots der Anwendbarkeit von Standard C ++ umgezogen sind.)quelle
Sie würden ein Element auch auf dem Heap speichern, wenn es außerhalb des Bereichs der Funktion verwendet werden muss, in der es erstellt wird. Eine mit Stapelobjekten verwendete Redewendung heißt RAII. Dabei wird das stapelbasierte Objekt als Wrapper für eine Ressource verwendet. Wenn das Objekt zerstört wird, wird die Ressource bereinigt. Stapelbasierte Objekte lassen sich leichter verfolgen, wann Sie möglicherweise Ausnahmen auslösen. Sie müssen sich nicht darum kümmern, ein Heap-basiertes Objekt in einem Ausnahmebehandler zu löschen. Aus diesem Grund werden Rohzeiger in modernem C ++ normalerweise nicht verwendet. Sie würden einen intelligenten Zeiger verwenden, der ein stapelbasierter Wrapper für einen Rohzeiger auf ein Heap-basiertes Objekt sein kann.
quelle
Um die anderen Antworten zu ergänzen, kann es auch um Leistung gehen, zumindest ein wenig. Nicht, dass Sie sich darüber Sorgen machen sollten, es sei denn, es ist für Sie relevant, aber:
Das Zuweisen im Heap erfordert das Finden einer Verfolgung eines Speicherblocks, was keine zeitkonstante Operation ist (und einige Zyklen und Overhead benötigt). Dies kann langsamer werden, wenn der Speicher fragmentiert wird und / oder Sie fast 100% Ihres Adressraums nutzen. Andererseits sind Stapelzuweisungen zeitkonstante, im Grunde "freie" Operationen.
Eine andere zu berücksichtigende Sache (wiederum wirklich nur wichtig, wenn es zu einem Problem wird) ist, dass normalerweise die Stapelgröße fest ist und viel niedriger als die Heap-Größe sein kann. Wenn Sie also große oder viele kleine Objekte zuweisen, möchten Sie wahrscheinlich den Heap verwenden. Wenn Ihnen der Stapelspeicherplatz ausgeht, löst die Laufzeit die Site-Titelausnahme aus. Normalerweise keine große Sache, aber eine andere Sache zu beachten.
quelle
Der Stapel ist effizienter und einfacher zu verwalten.
Heap sollte jedoch für alles verwendet werden, was größer als ein paar KB ist (in C ++ ist es einfach, erstellen Sie einfach einen
boost::scoped_ptr
auf dem Stapel, um einen Zeiger auf den zugewiesenen Speicher zu halten).Stellen Sie sich einen rekursiven Algorithmus vor, der immer wieder in sich selbst aufruft. Es ist sehr schwer, die Gesamtstapelnutzung zu begrenzen oder zu erraten! Während auf dem Heap der Allokator (
malloc()
odernew
) durch ZurückgebenNULL
oderthrow
Ing auf Speichermangel hinweisen kann .Quelle : Linux-Kernel, dessen Stack nicht größer als 8 KB ist!
quelle
std::unique_ptr
, die jeder externen Bibliothek wie Boost vorzuziehen ist (obwohl dies im Laufe der Zeit dem Standard zugute kommt).Der Vollständigkeit halber können Sie den Artikel von Miro Samek über die Probleme bei der Verwendung des Heaps im Kontext eingebetteter Software lesen .
Ein Haufen Probleme
quelle
Die Wahl, ob auf dem Heap oder auf dem Stapel zugewiesen werden soll, wird für Sie getroffen, abhängig davon, wie Ihre Variable zugewiesen wird. Wenn Sie etwas dynamisch mithilfe eines "neuen" Aufrufs zuweisen, weisen Sie es vom Heap aus zu. Wenn Sie etwas als globale Variable oder als Parameter in einer Funktion zuweisen, wird es auf dem Stapel zugewiesen.
quelle
Meiner Meinung nach gibt es zwei entscheidende Faktoren
Ich würde in den meisten Fällen lieber Stack verwenden, aber wenn Sie Zugriff auf Variablen außerhalb des Gültigkeitsbereichs benötigen, können Sie Heap verwenden.
Um die Leistung bei der Verwendung von Heaps zu verbessern, können Sie die Funktionalität auch zum Erstellen von Heap-Blöcken verwenden. Dies kann dazu beitragen, die Leistung zu steigern, anstatt jede Variable an einem anderen Speicherort zuzuweisen.
quelle
wahrscheinlich wurde dies recht gut beantwortet. Ich möchte Sie auf die folgende Artikelserie verweisen, um ein tieferes Verständnis für Details auf niedriger Ebene zu erhalten. Alex Darby hat eine Reihe von Artikeln, in denen er Sie mit einem Debugger durchführt. Hier ist Teil 3 über den Stapel. http://www.altdevblogaday.com/2011/12/14/cc-low-level-curriculum-part-3-the-stack/
quelle