Hallo
Wie kann ich so etwas:
int* pStructure = new int[1];
.. während der Laufzeit noch vergrössern, ohne einen vollkommen neuen Speicherbereich zu reservieren?
Einfach so, dass die vorhandenen Daten so bleiben wie sie sind und das Array noch etwas mehr Platz hat.
Ich möchte das nach Möglichkeit ohne std::vector machen.
Schon mal Danke!
Programmieren - alles kontrollieren 4.941 Themen, 20.715 Beiträge
Wenn deine sorge ist, dass sich dabei der zeiger "pStructure" ändert, dann hast du vermutlich keine chance, eine stabile lösung zu bekommen, wenn das aber nicht das problem ist, dann kannst du mit z.b.:
int *newP=new int[newsize];
for(int i=0;i<oldsize;i++) newP[i]=pStructure[i];
//bzw. in unkritischen fällen wie hier,
//wo keine internen adressen angepasst werden müssen
memcpy(newP, pStructure, sizeof(int)*oldsize);
delete[] pStructure;
pStructure=newP;
Die größe anpassen (neben newsize muss dann aber auch oldsize bekannt sein).
mr.escape
In meinem Fall ist es wohl am besten, wenn ich das Array stufenweise vergrössere.
Das kann ich so machen, wie du es beschreibst.
Danke!
Aber wähle die stufen nicht zu klein. Am besten mit zwei variablen für verwendet und alloziert. Wenn dann der platz nicht mehr reicht, einfach ein großes stück mehr allozieren.
Im beispiel wäre dann "oldsize" die benutzte größe und "newsize" die allozierte.
Ist alles verbraucht, dann kann so etwas passieren:
if(oldsize>=newsize){
int *newP=new int[newsize+1000];//z.b.
//hier fehlerbehandlung
newsize+=1000;
for(int i=0;i<oldsize;i++) newP[i]=pStructure[i];
//bzw. in unkritischen fällen wie hier,
//wo keine internen adressen angepasst werden müssen
memcpy(newP, pStructure, sizeof(int)*oldsize);
delete[] pStructure;
pStructure=newP;
}
mr.escape
Danke, so werde ich das machen.
Heute wollte ich diesen Code noch in einer Funktion kapseln.
Warum erhalte ich dort beim Aufruf von delete[] immer einen Debug Assertion Fehler? Wenn ich den Code direkt in die Main-Datei kopiere, klapp's!
// Utilities.cpp
void CUtilities::ResizeArray(int* p_iArray, int iNewSize, int iOldSize)
{
int* NewArray = new int[iNewSize];
for(int i=0;i NewArray[i]=p_iArray[i];
delete[] p_iArray;
p_iArray = NewArray;
}
// Testdatei Main.cpp
#include
Was auf jeden fall falsch ist, ist die übergabe von p_iArray in CUtilities::ResizeArray. Der parameter ist die alte adresse des arrays, dessen inhalt in einen neu allozierten bereich kopiert wird. Dieser neue bereich geht aber verloren, weil die übergabe der adresse "by value" erfolgt, d.h. nur die kopie der adresse angepasst wird und diese mit der beendigung der funktion verloren geht (sog. automatische stackvariable).
Stattdessen muss die adresse der adresse übergeben werden, also int** p_iArray.
Das ergibt:
void CUtilities::ResizeArray(int** p_iArray, int iNewSize, int iOldSize){
int* NewArray = new int[iNewSize];
for(int i=0;i<iOldSize;i++)
NewArray[i]=(*p_iArray)[i];
delete[] *p_iArray;
*p_iArray = NewArray;
}
und beim aufruf
Utilities.ResizeArray(&p_iArray, 20, 300);
Das ist auch die ursache für den fehler, weil die alte adresse, die in der vergrößerungsfunktion eben nicht angepasst wird, immer noch auf den freigegebenen bereich verweist. Der zugriff darauf löst dann den fehler aus.
Noch einfacher ist in diesem fall aber die verwendung von referenzen. Die einzige änderung ist ein "&" in:
void CUtilities::ResizeArray(int *&p_iArray, int iNewSize, int iOldSize)
Damit wird die parameterübergabe für diesen parameter von "by value" auf "by reference" umgestellt.
Für den compiler ist es ziemlich genau das selbe, wie bei der "doppelten adresse", es ist aber im gegensatz dazu nicht möglich die referenz selbst zu ändern, sondern nur die referenzierten objekte. Die folge ist eine geringere flexibilität bei komplexeren arrays.
mr.escape
Ich habe mir das mit den Zeigern aufgezeichnet, ganz begrieife ich es aber noch nicht.
Ich übergebe den Zeiger, der auf mein Array zeigt.
Dann kopiere ich die Daten aus dem Array in das neue.
Jetzt lösche ich das Array, auf das mein Zeiger zeigte.
Dem Zeiger gebe ich die Adresse der neuen Daten.
Im der Main.cpp habe ich aber immer noch den alten Zeiger auf den gelöschten Speicherbereich.
Passiert nicht genau das auch bei der Zeiger auf Zeiger Variante von dir?
Der Zeiger ändert sich immer noch nicht, oder doch?
Ich hab das jedenfalls mal so geänder, wie du es beschrieben hast, es funktioniert leider immer noch nicht.
Ich glaube aber, dass es noch gar nicht zu diesem Fehler kommt, sondern schon vorher was passiert.
Das Beispiel stürzt nämlich genau bei delete[] ab, dort wird ja noch nicht auf das zurückgegebene Array zugegriffen...
EDIT: ARGH, sorry!!
Ich hab die Parameter für Oldsize und NewSize verwechselt... *Kopf gegen die Wand hau*
Der erste Teil meiner Frage gilt aber noch ;-)
EDIT2: Ich sehe gerade, dass meine erste Variante auch funktioniert.
Wieso jetzt das?
Das mit dem nicht aktuellen Zeiger klang für mich doch auch ganz logisch!? Das kann doch eigentlich gar nicht gehen!
[Diese Nachricht wurde nachträglich bearbeitet.]
Ich übergebe den Zeiger, der auf mein Array zeigt.
Eben nicht, sondern eine kopie des inhaltes, d.h. die adresse des arrays.
Dann kopiere ich die Daten aus dem Array in das neue.
Jetzt lösche ich das Array, auf das mein Zeiger zeigte.
Dem Zeiger gebe ich die Adresse der neuen Daten.
Ja, aber dem zeiger mit der kopie der alten adresse. Das irritierende ist, dass die variablen beide gleich heißen (p_iArray).
Nicht die variable von int* p_iArray = new int[20]; wird geändert, sondern die aus void CUtilities::ResizeArray(int* p_iArray, int iNewSize, int iOldSize)
Im der Main.cpp habe ich aber immer noch den alten Zeiger auf den gelöschten Speicherbereich.
Exakt. Das ist eine andere variable mit dem gleichen namen.
Passiert nicht genau das auch bei der Zeiger auf Zeiger Variante von dir?
Nein, denn hier wird nicht die adresse des arrays übergeben, sondern die adresse der stelle, wo die adresse des arrays gespeichert ist. In p_iArray aus void CUtilities::ResizeArray(int** p_iArray, int iNewSize, int iOldSize) steht also die adresse der adresse. Wenn nun die zuweisung (*p_iArray = NewArray;) kommt, dann wird genau dort, wo die alte adresse steht, die neue hingeschrieben.
Der aufruf von CUtilities::ResizeArray ist definitiv falsch, weil nur die kopie der adresse verändert wird und dieser wert beim rücksprung verloren geht.
Ich sehe gerade, dass meine erste Variante auch funktioniert.
Wieso jetzt das?
Dass es jetzt scheinbar geht, liegt daran, dass einfach illegale, aber vorhandene speicherbereiche überschrieben werden. Das freigeben per delete[] löscht nicht den speicher, sondern gibt diesen nur für andere verwendungen frei, so in etwa wie beim normalen löschen von dateien.
mr.escape
Vielen Dank für die ausführlichen Antworten und deine investierte Zeit!
Mit dieser Erklärung habe ich es jetzt begriffen.
Sobald ich zu Hause bei meinem Code bin, werde ich es ändern, dann gehts weiter mit Templates. :)