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?
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?
Antworten:
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.
free
weiß, 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.
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[]
.
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)*5
Bytes 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 i
auf 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)
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.
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.
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
.
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?
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.
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!