Woher "kennt" delete [] die Größe des Operandenarrays?

250
Foo* set = new Foo[100];
// ...
delete [] set;

Sie übergeben die Grenzen des Arrays nicht an delete[]. Aber wo werden diese Informationen gespeichert? Ist es standardisiert?

VolkerK
quelle
sourceforge.net/projects/fastmm ist Open Source und ersetzt den Speichermanager. Hier erfahren Sie, wie die Speicherverwaltung funktioniert und woher die Informationen zum Zuweisen und Löschen von Speichern stammen.
1
Beachten Sie, dass FastMM nur für die Delphi / C ++ Builder-Compiler spezifisch ist und kein allgemeiner Speichermanager für C ++ ist. Es ist nicht einmal in C ++ geschrieben.
Remy Lebeau

Antworten:

181

Wenn Sie Speicher auf dem Heap zuweisen, verfolgt Ihr Allokator, wie viel Speicher Sie zugewiesen haben. Dies wird normalerweise in einem "Kopf" -Segment unmittelbar vor dem Speicher gespeichert, den Sie zuweisen. Auf diese Weise weiß der De-Allokator genau, wie viel Speicher freigegeben werden muss, wenn es Zeit ist, den Speicher freizugeben.

Peter Kühne
quelle
4
Beachten Sie, dass dies nur für Array-Zuordnungen in C ++ gilt. Alle anderen Zuordnungen hängen von der Größe des Typs ab. Einige Bibliotheken speichern alle Zuordnungsgrößen, normalerweise nur im Debugging-Modus.
Zan Lynx
97
Es gibt absolut keinen Grund, warum diese Informationen dem Programmierer nicht zur Verfügung stehen sollten. Ich kann einen Zeiger auf eine Funktion übergeben und freigeben, aber um die Größe selbst in derselben Funktion zu erhalten, muss ich einen zusätzlichen Parameter übergeben. Macht das irgendeinen Sinn?
Mark Ruzon
26
@ Mark, es macht einen kleinen Menge Sinn, denn in der Theorie den Allocator entbindet immer speichern die Größe des zugeordneten Blocks (die von der Größe des abweichen können angeforderten Block). Bestimmte Allokator-Designs benötigen diese Informationen möglicherweise für ihre eigenen Zwecke oder sind möglicherweise nicht hoch genug, um Typinformationen zum Verfolgen der Größe von Nicht-Array-Heap-Zuordnungen usw. zu verwenden. Erzwingen, dass der Allokator die angeforderte Größe speichert (damit Sie dies nicht tun) Die Array-Größe muss selbst übergeben werden. Dies ist möglicherweise eine kleine Belastung, kann jedoch Auswirkungen auf die Leistung der denkbaren Allokator-Designs haben.
Doug McClean
33
Entschuldigung, aber diese Antwort verfehlt den Punkt. Was QuantumPete beschreibt, ist im Grunde "Woher freeweiß, wie viel Speicher freigegeben werden muss". Ja, die Speicherblockgröße wird "irgendwo" von malloc(normalerweise im Block selbst) gespeichert free. Allerdings new[]/ delete[]ist eine andere Geschichte. Letztere arbeiten grundsätzlich über malloc/ free. new[]speichert auch die Anzahl der Elemente, die er erstellt hat, im Speicherblock (unabhängig von malloc), damit er später delete[]diese Nummer abrufen und verwenden kann, um die richtige Anzahl von Destruktoren aufzurufen.
Am
26
Dh physisch werden zwei Zähler im Block gespeichert: Blockgröße (nach malloc) und Elementanzahl (nach new[]). Es ist zu beachten, dass das erstere nicht zur Berechnung des letzteren verwendet werden kann, da im Allgemeinen die Größe des Speicherblocks größer sein kann, als für das Array der angeforderten Größe wirklich erforderlich ist. Beachten Sie auch, dass der Array-Elementzähler nur für Typen mit nicht trivialem Destruktor benötigt wird. Bei Typen mit trivialem Destruktor wird der Zähler nicht von gespeichert new[]und natürlich nicht von abgerufen delete[].
AnT
23

Einer der Ansätze für Compiler besteht darin, etwas mehr Speicher zuzuweisen und eine Anzahl von Elementen in einem Kopfelement zu speichern.

Beispiel, wie es gemacht werden könnte:

Hier

int* i = new int[4];

Der Compiler weist sizeof(int)*5Bytes zu.

int *temp = malloc(sizeof(int)*5)

Speichert "4" in den ersten sizeof(int)Bytes

*temp = 4;

und setzen i

i = temp + 1;

Zeigt also iauf ein Array von 4 Elementen, nicht auf 5.

Und Löschung

delete[] i;

wird wie folgt verarbeitet:

int *temp = i - 1;
int numbers_of_element = *temp; // = 4
... call destructor for numbers_of_element elements
... that are stored in temp + 1, temp + 2, ... temp + 4 if needed
free (temp)
Avt
quelle
9

Die Informationen sind nicht standardisiert. Auf den Plattformen, an denen ich gearbeitet habe, werden diese Informationen jedoch kurz vor dem ersten Element gespeichert. Daher könnten Sie theoretisch darauf zugreifen und es inspizieren, aber es lohnt sich nicht.

Dies ist auch der Grund, warum Sie delete [] verwenden müssen, wenn Sie Speicher mit new [] zugewiesen haben, da die Array-Version von delete weiß, dass (und wo) sie suchen muss, um die richtige Speichermenge freizugeben - und die entsprechende Anzahl von Destruktoren aufzurufen für die Objekte.

Daemin
quelle
5

Grundsätzlich ist es im Gedächtnis angeordnet als:

[info] [mem, nach dem du gefragt hast ...]

Wobei info die Struktur ist, die Ihr Compiler zum Speichern der zugewiesenen Speichermenge verwendet, und was nicht.

Dies ist jedoch implementierungsabhängig.

Francisco Soto
quelle
4

Dies ist nicht in der Spezifikation enthalten - es hängt von der Implementierung ab.

jeffm
quelle
3

Es ist im C ++ - Standard als compilerspezifisch definiert. Was Compilermagie bedeutet. Es kann mit nicht trivialen Ausrichtungsbeschränkungen auf mindestens einer Hauptplattform brechen.

Sie können über mögliche Implementierungen nachdenken, indem Sie erkennen, dass dies delete[]nur für Zeiger definiert ist new[], die von zurückgegeben werden und möglicherweise nicht derselbe Zeiger sind, der von zurückgegeben wird operator new[]. Eine Implementierung in freier Wildbahn besteht darin, die Anzahl der Arrays im ersten von zurückgegebenen int zu speichern operator new[]und new[]einen darüber hinausgehenden Zeigerversatz zurückzugeben. (Aus diesem Grund können nicht triviale Ausrichtungen unterbrochen werden new[].)

Denken Sie daran, dass operator new[]/operator delete[]! = new[]/delete[].

Außerdem ist dies orthogonal dazu, wie C die Größe des von zugewiesenen Speichers kennt malloc.

MSN
quelle
2

Weil das Array, das 'gelöscht' werden soll, mit einer einzigen Verwendung des Operators 'new' erstellt worden sein sollte. Die 'neue' Operation sollte diese Informationen auf den Heap gelegt haben. Wie würden sonst zusätzliche Verwendungen von new wissen, wo der Heap endet?

Joel Coehoorn
quelle
0

Es ist nicht standardisiert. In der Laufzeit von Microsoft verwendet der neue Operator malloc () und der Löschoperator free (). In dieser Einstellung entspricht Ihre Frage also der folgenden: Woher kennt free () die Größe des Blocks?

Hinter den Kulissen, dh in der C-Laufzeit, findet eine Buchhaltung statt.

Andre
quelle
5
Nicht wahr. Vor dem Aufruf von free muss delete [] zuerst Destruktoren aufrufen. Dafür reicht es nicht aus, die Gesamtzuordnungsgröße zu kennen. Tatsächlich funktioniert new [] und delete [] für einfache und zerstörte Typen in VC ++ unterschiedlich.
Suma
0

Dies ist ein interessanteres Problem, als Sie zunächst vielleicht denken. Diese Antwort bezieht sich auf eine mögliche Implementierung.

Erstens, während Ihr System auf einer bestimmten Ebene wissen muss, wie der Speicherblock "freigegeben" wird, merkt sich das zugrunde liegende malloc / free (das new / delete / new [] / delete [] im Allgemeinen aufruft) nicht immer genau, wie viel Speicher vorhanden ist Wenn Sie danach fragen, kann es aufgerundet werden (wenn Sie beispielsweise über 4 KB liegen, wird es häufig auf den nächsten Block mit 4 KB aufgerundet).

Selbst wenn die Größe des Speicherblocks ermittelt werden könnte, sagt dies nicht aus, wie viele Werte sich im neuen Speicher befinden, da dieser kleiner sein kann. Daher müssen wir eine zusätzliche Ganzzahl speichern, die uns sagt, wie viele Werte es gibt.

AUSSER, wenn der zu erstellende Typ keinen Destruktor hat, muss delete [] nichts anderes tun, als den Speicherblock freizugeben, und muss daher nichts speichern!

Chris Jefferson
quelle