2016-04-02 12 views
4

Zum Beispiel:Ist in C++ die Längeninformation eines dynamischen Arrays mit dem Zeiger oder der Adresse des ersten Elements verknüpft?

int *a, *b; 
a = new int[10]; 
b = new int(12); 
b = a; // I know there's memory leak, but let's ignore it first 
delete [] b; // line L 

Was wird passieren? Wird das gesamte Array erfolgreich gelöscht?

Was ist, wenn die Zeile L dadurch ersetzt wird: b = a + 1; löschen [] b;

Oder von diesem: a ++; löschen [] a;

Und schließlich, wenn die Länge eines dynamischen Array mit der Startadresse zugeordnet ist, oder mit anderen Worten, mit dem Array selbst verbunden, haben wir irgendeine Möglichkeit, die Länge zu erhalten, ohne eine andere Variable zu speichern die Länge?

Vielen Dank!

+0

Die Längeninformationen sind verloren, so einfach. Wenn Sie den Überblick behalten möchten, verwenden Sie stattdessen 'std :: vector '. Intern 'new'' delete' wird die Buchhaltung der Adresse des ersten Elements durchgeführt. –

+0

Mein g ++ signalisiert "free(): invalid pointer" wenn (b = a + 1; delete [] b;) und delete [] nachdem a ++ wie undefiniertes Verhalten aussieht, da Sie versuchen werden, auf freien Speicher zuzugreifen. – user3655463

Antwort

5

Die Informationen zur Speicherblockgröße und zur Arraylänge sind der Adresse des Objekts zugeordnet, die die Adresse des ersten Elements des Arrays ist.

I.e. Ihre delete[] ist im gegebenen Code

int *a, *b; 
a = new int[10]; 
b = new int(12); 
b = a; // I know there's memory leak, but let's ignore it first 
delete [] b; // line L 

jedoch sicher, dort zu Zugang die zugehörigen Informationen keine tragbare Art und Weise ist. Es ist ein Implementierungsdetail. Verwenden Sie eine std::vector, wenn Sie solche Informationen benötigen.


Um zu verstehen, warum die Informationen nicht zugegriffen werden kann, beachten Sie zuerst die Speicherblockgröße für Freigabe benötigt wird, kann als die Größe des Array-Element Array-Länge mal größer sein. Es handelt sich also um zwei getrennte Werte. Und für ein Array von POD-Elementtypen, bei denen Elementdestruktoraufrufe nicht erforderlich sind, muss kein explizit gespeicherter Arraylängenwert vorhanden sein.

Selbst für ein Array, in dem Destruktoraufrufe erforderlich sind, muss kein explizit gespeicherter Array-Längenwert mit Array verknüpft sein. Zum Beispiel kann der Compiler im Code des Musters p = new int[n]; whatever(); delete[] p; im Prinzip wählen, die Array-Länge an irgendeinen nicht verwandten Ort zu setzen, wo durch den für den Ausdruck delete erzeugten Code leicht darauf zugegriffen werden kann. Z.B. es könnte in ein Prozessorregister eingetragen werden.

Abhängig davon, wie intelligent der Compiler ist, muss auch nicht unbedingt eine explizit gespeicherte Speicherblockgröße vorhanden sein. Zum Beispiel kann der Compiler bemerken, dass in einer Funktion f drei Arrays a, b und c zugeordnet sind, wobei ihre Größen bei der ersten Zuweisung bekannt sind und alle drei am Ende freigegeben sind. Der Compiler kann also die drei Zuweisungen durch einen einzigen ersetzen und ebenso die drei Zuweisungen durch einen einzigen ersetzen (diese Optimierung wird explizit von C++ 14 §5.3.4/10 erlaubt).


Außer in C++ 14 und später in einer Aufhebung der Zuordnung Funktion, die ein wenig zu spät ist.
C++ 14 §5.3.4/10: “ Eine Implementierung darf einen Aufruf einer ersetzbaren globalen Zuweisungsfunktion (18.6.1.1, 18.6.1.2) weglassen. Wenn dies der Fall ist, wird der Speicher stattdessen von der Implementierung bereitgestellt oder durch Erweiterung der Zuweisung eines anderen new-expression bereitgestellt. Die Implementierung kann die Zuweisung eines neuen Ausdrucks erweitern, um Speicher für einen neuen Ausdruck zur Verfügung zu stellen. ”

+0

Vielen Dank! Ich habe noch zwei Fragen zu deinen Antworten. Erstens, können Sie erklären, warum die Blockgröße größer als die Größe des Arrays sein könnte? Zweitens meinten Sie, dass, obwohl die Array-Länge irgendwo gespeichert ist (für ein Nicht-POD-Array) und der "Löschen" -Ausdruck diese Information basierend auf dem Wert des auf das Array zeigenden Zeigers finden kann, diese Information für andere Codes nicht zugänglich ist ? –

+1

Das Finden und Zurückgeben einer perfekteren Übereinstimmung für eine Zuweisungsanforderung oder das Teilen eines freien Blocks, um dies auszuführen, verursacht Kosten in der Ausführungszeit und der Speicherfragmentierung. In Windows verwalten die virtuellen Speicherfunktionen den Speicher effizient in Form von 4 KB Blöcken, die als Seiten bezeichnet werden. Aber die Zuweisung von 4KB, wenn Sie nur 42 Bytes benötigen, ist ein wenig verschwenderisch! Normalerweise verwendet eine Anwendung eine API auf höherer Ebene, die zusätzliche Arbeit leistet, um eine ** Granularität ** des unteren Blocks zu erzielen, angeblich 8 Bytes bei 32-Bit-Systemen und 16 Bytes bei 64-Bit-Systemen. Die C-Laufzeitumgebung kann eine zusätzliche Ebene bereitstellen. Fügen Sie dazu Ausrichtungsanforderungen hinzu. –

+1

Wenn eine Array-Länge irgendwo im Rahmen der Auswertung eines 'new []' -Ausdrucks gespeichert wird, ist diese Information im Allgemeinen nicht über den standardmäßigen portablen Code zugänglich, ja. –

1

die C++ Standard sagt nur, dass delete[] auf einem mit new zugeordnet Zeigern und unter Verwendung delete auf einem Zeiger zugewiesen mit new[] undefiniertem Verhalten ist.

So funktioniert dies, oder auch nicht, je nach Implementierungen. Es kann Ihr Haus in Brand setzen.

Nehmen wir zum Beispiel an, dass diese Funktionen auf einem zugrunde liegenden Puffer mit malloc() und free() basieren.

new und delete wird, in den meisten Implementierungen verwenden einen Puffer, der genau die Größe und die Adresse des Elements hat (hier ein int)

new[] und delete[] sind komplexer. Sie müssen im Puffer nicht nur size Elemente, sondern auch den Wert size speichern. Eine Implementierung könnte size vor den tatsächlichen Elementen speichern. Dies würde bedeuten, dass der Zeiger des zugrunde liegenden Puffers nicht derselbe ist wie der Zeiger auf das erste Element, was der Wert ist, der von ungültigen Zeigern, d. H Zeiger, die von malloc nie zurückgegeben wurden. Dies wird abstürzen oder eine Ausnahme auslösen

Natürlich ist dies alles Implementierung definiert.

+2

** - 1 ** "Dies kann funktionieren oder nicht, je nach Implementierung. Es kann dein Haus in Brand setzen. "Ist einfach falsch. Hast du den Code falsch verstanden? –

+0

Warum ist es falsch? Bitte erkläre. Für mich ist das UB, und mein Satz, den du zitiert hast, erklärt nur UB. – galinette

+4

In dem OP-Code wird 'delete []' an einem Zeiger aufgerufen, der mit 'new []' belegt ist. Ja, "b" wird ursprünglich mit "new" zugewiesen, aber nach "b = a" zeigt es auf ein Array, das mit 'new []' belegt ist. – TonyK

2
int *a, *b; 
a = new int[10]; 
b = new int(12); 
b = a; // I know there's memory leak, but let's ignore it first 
delete [] b; // line L 

Wird das gesamte Array erfolgreich gelöscht werden?

Ja, wird Speicher erfolgreich freigegeben werden, da der Zeiger a eingestellt wurde auf ein Array von 10 ganzen Zahlen Adresse in b gespeichert

a = new int[10]; 

dann die gleiche Speicherstelle hinzuweisen

b = a; 

daher die delete[] Zeile korrekt deallocates das Array Chunk

delete [] b; 

Wie Sie festgestellt haben, leckt der Code auch die Ganzzahlvariable 12, die ursprünglich zugewiesen wurde und auf die b zeigt.

Als Nebenbemerkung delete[] Aufruf Speicher frei nicht mit einem Array zugeordnet (genauer gesagt mit einem statischen Typenkonflikt - siehe [expr.delete]/p3) undefiniertes Verhalten ausgelöst hätte.


Jetzt für einige Interna auf, wo ist die Größe Informationen bei der Zuweisung von Speicher gespeichert.

Compiler haben einen gewissen Freiheitsgrad (vgl. §5.3.4/10), wenn es um Speicherzuweisungsanforderungen geht (z. B. können sie Speicherzuordnungsanforderungen "gruppieren").

Wenn Sie new ohne mitgelieferten allocator nennen, sei es malloc nennen oder nicht, ist es Implementierung

definiert

[new.delete.single]

eine Schleife Führt: Innerhalb von Schleife versucht die Funktion zuerst, den angeforderten Speicher zuzuordnen. Ob der Versuch einen Aufruf der Standard-C-Bibliotheksfunktion malloc beinhaltet, ist nicht angegeben.

Unter der Annahme, es ruft malloc() (auf den letzten Windows-Systemen den UCRT nennt), versucht, Speicherplatz zuweisen bedeutet mit paged virtual memory Buchführung zu tun, und das ist, was malloc in der Regel der Fall ist.

Ihre Größeninformationen werden zusammen mit anderen Daten zur Zuweisung des von Ihnen angeforderten Speichers weitergegeben.

C-Bibliotheken wie glibc kümmern sich um Ausrichtung, Speicherüberwachung und Zuweisung einer neuen Seite (falls erforderlich), damit diese über einen Systemaufruf in den Kernel gelangen kann.

Es gibt keinen "Standard" Weg, diese Informationen nur von der Adresse zurück zu bekommen, da es ein Implementierungsdetail ist. Wie viele haben vorgeschlagen, wenn Sie eine solche Informationen benötigen, so könnte man

  • speichern die Größe irgendwo
  • Verwendung sizeof() mit einem Array-Typ
  • noch besser für C++: Verwenden Sie eine std::vector<>

Diese Information ist außerdem mit einer Speicherzuweisung verbunden, in beiden Ihren sekundären Fällen (dh die L Zeile wird durch

ersetzt 0

oder

a++; delete [] a; 

wird in Tränen enden, da diese Adressen nicht mit einer gültigen Zuordnung zugeordnet ist.

+0

Danke für Ihre klare Antwort. Wie für die Größe Info, ist es irgendwo gespeichert, aber es gibt keine Schnittstelle, um den Wert abzurufen, ist das richtig? –

+1

@FrankShen [einige Funktionen] (http://man7.org/linux/man-pages/man3/malloc_userable_size.3.html) von der gleichen CRT zur Verfügung gestellt werden könnte verwendet werden, aber wie gesagt, es ist nicht einmal garantiert, dass "neu 'benutzt' malloc'. Plus wird die Größe des Blocks zurückgeben (d. H. Könnte größer sein als Ihre angeforderte Größe aufgrund dessen, was ich gerade oben geschrieben habe). Ich würde sagen, kein zuverlässiger und standardmäßiger Weg. –