2015-11-14 9 views
24

Von dem, was here geschrieben ist, weist new in freien Speicher während mallocHaufen verwendet und die beiden Begriffe bedeuten oft das Gleiche.Ist es sicher, Speicher neu zuzuweisen?

Von was here geschrieben wird, kann realloc den Speicherblock an einen neuen Speicherort verschieben. Wenn Free Store und Heap zwei verschiedene Speicherbereiche sind, bedeutet das dann ein Problem?

Insbesondere würde ich gerne wissen, ob es sicher ist,

int* data = new int[3]; 
// ... 
int* mydata = (int*)realloc(data,6*sizeof(int)); 

Wenn nicht zu verwenden, gibt es eine andere Art und Weise zu realloc mit new sicher zugewiesenen Speicher? Ich könnte neuen Bereich und memcpy den Inhalt zuweisen, aber von dem, was ich verstehe realloc kann den gleichen Bereich verwenden, wenn möglich.

+13

Verwenden Sie einfach einen 'Vektor'. –

+8

@KarolyHorvath Woher wissen Sie, dass das in jedem Fall ein gangbarer Weg ist? Was ist mit der Bereitstellung eines eingebetteten Systems ohne Standardbibliotheksunterstützung? Was ist mit der Integration mit einer C-Schnittstelle, die eine Realloc durchführen kann? –

+6

@KarolyHorvath Sie können gerne messen, wie viel Zeit benötigt wird, um 200 MB Speicher mit 'malloc' (wenige Mikrosekunden) vs.' std :: vector' (~ 200 Millisekunden!) Zuzuweisen. 'std :: vector' ist keine magische Lösung für jedes Speicherproblem –

Antwort

39

Sie können nur realloc das, was über malloc zugewiesen wurde (oder Familie, wie calloc).

Das liegt daran, dass die zugrunde liegenden Datenstrukturen, die freie und belegte Speicherbereiche verfolgen, sehr unterschiedlich sein können.

Es ist wahrscheinlich aber keineswegs, dass garantiert C++ new und C malloc den gleichen zugrunde liegenden allocator verwenden, wobei in diesem Fall realloc für beide funktionieren konnte. Aber formal ist das in UB-Land. Und in der Praxis ist es einfach unnötig riskant.


C++ bietet keine Funktionalität realloc entspricht.

Am nächsten ist die automatische Neuzuweisung von (den internen Puffern von) Containern wie std::vector.

Die C++ - Container leiden unter einer Konstruktion, die die Verwendung von realloc ausschließt.


Anstelle des präsentierten Codes

int* data = new int[3]; 
//... 
int* mydata = (int*)realloc(data,6*sizeof(int)); 

& hellip; Dazu:

vector<int> data(3); 
//... 
data.resize(6); 

Wenn Sie jedoch unbedingt die allgemeine Effizienz der realloc brauchen, und wenn Sie new für die ursprüngliche Zuordnung zu akzeptieren, dann ist Ihre einzige Möglichkeit für Effizienz ist Compiler-spezifische Mittel zu nutzen, Wissen, dass realloc ist sicher mit diesem Compiler.

Andernfalls, wenn Sie unbedingt die allgemeine Effizienz der realloc müssen, ist aber nicht new akzeptieren gezwungen, dann können Sie malloc und realloc verwenden. Mit intelligenten Zeigern erhalten Sie dann die gleiche Sicherheit wie mit C++ - Containern.

+1

das von dir geschriebene Snippet ist die idiomatischste Art, Speicher in C++ neu zuzuteilen, aber es ist ein sicherer Weg um deine Leistung zu töten, wenn du auf diesem Feld bist. –

+2

@KyleStrand: Wenn Sie 'new' für die ursprüngliche Zuweisung akzeptieren müssen, dann ist Ihr einziger Rückgriff auf Effizienz Compiler-spezifische Mittel zu verwenden. Z.B. Wissen, dass 'Realloc' mit diesem Compiler sicher ist. Andernfalls können Sie Smartpointer mit 'malloc' und' realloc' verwenden. Wie auch immer, erinnere dich an die erste (und zweite) Regel der Optimierung, nämlich an ** MEASURE **. –

+0

Danke. Ich habe meinen Kommentar gelöscht, weil Ihr Beitrag meine Frage beantwortet hat. –

5

Es ist nicht sicher, und es ist nicht elegant.

Es könnte möglich sein, neue/delete zu überschreiben, um die Neuzuweisung zu unterstützen, aber dann können Sie auch überlegen, die Container zu verwenden.

+4

Ich bin mir nicht sicher, was an Realloc unelegant ist. –

+0

Verwenden von new/delete mit realloc, override oder anderen Mitteln, damit es funktioniert, ist nicht elegant, lesen Sie bitte das Thema. –

+0

Also meinst du das * weil * es nicht sicher ist, es ist unelegant zu versuchen * es * sicher zu machen? Das ist nicht klar aus deiner Antwort. Und gehe nicht davon aus, dass ich es irgendwie geschafft habe, einen Kommentar zu deiner Antwort zu hinterlassen, ohne "das Thema zu lesen"; das ist sinnlos beleidigend. –

5

Ja - wenn new tatsächlich an erster Stelle malloc genannt wird (zum Beispiel, so funktioniert VC++ new).

Nicht anders. Beachten Sie, dass der Code compilerspezifisch und nicht mehr zwischen Compilern übertragbar ist, wenn Sie sich entscheiden, den Speicher neu zuzuweisen (weil newmalloc aufgerufen wird).

(Ich weiß, diese Antwort kann viele Entwickler verärgern, aber ich antworte hängt von echten Fakten, nicht nur Idiomaticy).

+0

Ist das wahr für 'operator new []()', was hier verwendet wird, anstatt einfach 'operator new()'? –

+0

auf VC++ rufen alle üblichen "neuen" Operatoren "malloc" auf. –

+0

Ja, aber ich wäre überrascht, wenn das Ergebnis von 'operator new []' dasselbe wäre wie der Wert, der von einem Aufruf von 'malloc' zurückgegeben wird, weil die Zählung gespeichert wird. Und wenn nicht, dann kannst du es nicht an "realloc" weitergeben. –

7

Im Allgemeinen tun Sie das nicht. Wenn Sie benutzerdefinierte Typen mit nicht-triviale Initialisierung verwenden, wird der Destruktor Ihrer Objekte von realloc bei Neuzuweisung-Kopie-Freigabe nicht aufgerufen. Die Kopie Konstruktor wird beim Kopieren auch nicht genannt. Dies kann zu undefiniertem Verhalten aufgrund einer inkorrekten Verwendung von Objektlebensdauer führen (siehe C++ Standard §3.8 Objektlebensdauer, [basic.life]).

1 Die Lebensdauer eines Objekts ist eine Laufzeiteigenschaft des Objekts. Ein Objekt hat eine nicht-triviale Initialisierung, wenn es von einem Klassen- oder Aggregattyp ist und es oder eines seiner Mitglieder von einem anderen Konstruktor als einem trivialen Standardkonstruktor initialisiert wird. [Hinweis: Die Initialisierung durch einen trivialen Copy/Move-Konstruktor ist eine nicht-triviale Initialisierung. -Ende note]

Die Lebensdauer eines Objekts vom Typ T beginnt, wenn:

- Speicher mit der richtigen Ausrichtung und Größe für den Typ T erhalten wird, und

- wenn das Objekt nicht-triviale Initialisierung ist seine Initialisierung abgeschlossen.

Die Lebensdauer eines Objekts des Typs T endet, wenn:

- wenn T ein Klassentyp mit einer nicht-trivialen destructor (12.4), der Destruktor Anruf beginnt oder

- der Speicher, der Das Objekt wird wieder verwendet oder freigegeben.

Und später (Hervorhebung von mir):

3 Die auf Objekte in dieser Internationalen Norm zugeschriebenen Eigenschaften für ein bestimmtes Objekt anwenden nur während ihrer Lebensdauer.

Also, Sie wirklich nicht wollen, um ein Objekt verwenden aus seiner Lebensdauer.

4

Das ist nicht sicher. Zuerst muss der Zeiger, den Sie an realloc übergeben haben, von malloc oder realloc: http://en.cppreference.com/w/cpp/memory/c/realloc erhalten worden sein.

Zweitens muss das Ergebnis von new int [3] nicht dasselbe wie das Ergebnis der Zuweisungsfunktion sein - zusätzlichen Speicherplatz kann zugewiesen werden, um die Anzahl der Elemente zu speichern.

(Und für komplexere Typen als int würde realloc nicht sicher sein, da es nicht kopieren oder verschieben Bauer nicht nennen.)

3

Sie in der Lage sein kann (nicht in allen Fällen), aber Sie shouldn t. Wenn Sie die Datentabelle ändern müssen, sollten Sie stattdessen std::vector verwenden.

Einzelheiten zur Verwendung finden Sie in einer anderen SO question.

12

Die einzige möglicherweise relevanten Restriktions C++ zu realloc ergänzt ist, dass C++ 's malloc/calloc/realloc müssen nicht in Bezug auf ::operator new implementiert werden, und seine free müssen nicht in Bezug auf ::operator delete implementiert werden (pro C++ 14 [c. malloc] p3-4).

Dies bedeutet, dass die Garantie, die Sie suchen, in C++ nicht existiert. Es bedeutet jedoch auch, dass Sie ::operator new in Bezug auf malloc implementieren können. Und wenn Sie das tun, dann kann das Ergebnis ::operator new theoretisch an realloc übergeben werden.

In der Praxis sollten Sie über die Möglichkeit besorgt sein, dass das Ergebnis new nicht mit dem Ergebnis ::operator new übereinstimmt. C++ - Compiler können z.B. Kombinieren Sie mehrere new Ausdrücke, um einen einzelnen ::operator new Aufruf zu verwenden. Dies ist etwas, das Compiler bereits getan haben, als der Standard es nicht erlaubte, IIRC, und der Standard erlaubt es nun (nach C++ 14 [expr.new] p10). Das heißt, selbst wenn Sie diese Route gehen, haben Sie immer noch keine Garantie, dass Ihre new Zeiger auf realloc sinnvoll ist, auch wenn es kein undefiniertes Verhalten mehr ist.

+1

Bitte Referenzen für (1)" C++'s malloc/calloc/Realloc darf nicht im Sinne von :: operator new "implementiert werden, und für (2) über die in der Praxis noch nicht standardisierte Regel, dass" C++ - Compiler beispielsweise mehrere neue Ausdrücke kombinieren können, um einen einzigen :: operator new call zu verwenden ". –

+0

@ Cheersandthth.-Alf Eine Referenz für die erste hinzugefügt. Den eigentlichen Standardtext nicht enthalten, da dies keine [Sprachanwalts-] Frage ist. Ich habe kein Beispiel für mehrere "neue" Aufrufe, die die von mir beschriebenen Ergebnisse liefern, und ein schnelles und einfaches Beispiel, das nur den zugewiesenen Speicher löscht, kombiniert die Zuweisungen nicht zu einem einzigen, es optimiert nur die Zuweisungen vollständig. – hvd

0

Diese Funktion ist vor allem in C. verwendet

Memset setzt die Bytes in einem Speicherblock auf einen bestimmten Wert.

malloc weist einen Speicherblock zu.

Calloc, wie malloc. Einziger Unterschied ist, dass es die Bytes auf Null initialisiert.

In C++ ist die bevorzugte Methode zur Speicherzuweisung die Verwendung von new.

C: int intArray = (int *) malloc (10 * sizeof (int)); C++: int intArray = neuer int [10];

C: int intArray = (int *) Calloc (10 * sizeof (int)); C++: int intArray = neuer int10;

+2

Ich glaube nicht, dass dies die Frage beantwortet, weil es überhaupt keine Umverteilung anspricht. –

3

In der Regel keine.

Es gibt eine ganze Reihe von Dingen, die es sicher halten müssen:

  1. Bitwise Kopieren Sie die Art und die Quelle verlassen müssen sicher sein.
  2. Der Destruktor muss trivial sein, oder Sie müssen die Elemente, die Sie freigeben möchten, direkt vernichten.
  3. Entweder ist der Konstruktor trivial, oder Sie müssen die neuen Elemente direkt konstruieren.

Triviale Typen erfüllen die obigen Anforderungen.

Zusätzlich:

  1. Die new[] -function die Anfrage an malloc ohne Änderung passieren muss, noch auf der Seite jede Buchhaltung tun. Sie können dies erzwingen, indem Sie global new [] und delete [] oder die in den entsprechenden Klassen ersetzen.
  2. Der Compiler muss nicht mehr Speicher anfordern, um die Anzahl der zugewiesenen Elemente oder irgendetwas anderes zu speichern.
    Es gibt keine Möglichkeit, dies zu erzwingen, obwohl ein Compiler solche Informationen nicht speichern sollte, wenn der Typ einen trivialen Destruktor hat, in Bezug auf Qualität der Implementierung.
Verwandte Themen