Dynamische Arrays "verkleinern"
-
Hey Coders!
Ich hab mal ne kleine Frage, denn mein Programm schmiert in letzter zeit manchmal ab, was auf Speicherlecks zurückzuführen ist; Da ich schon wirklich jedes malloc(), realloc() und free() genaust geprüft habe, bin ich mir nicht sicher, ob es vielleicht daran liegen könnte, das ich einige dynamisch allokierte Arrays falsch freigebe.
Folgendes Beispiel: Ich habe eine kleine Struktur
struct test { char* ein_string; int blah; }; test* dyn_array = (test*)NULL; int dyn_array_cnt = 0;
mit folgender Routine lege ich das Array an bzw. erweitere es:
if(dyn_array_cnt == 0) { dyn_array = (test*)malloc(sizeof(test*)); } else { dyn_array = (test*)realloc((test*)dyn_array, (dyn_array_cnt+1) * sizeof(test)); } dyn_array[dyn_array_cnt].ein_string = strdup(irgendeinestringvariable); dyn_array[dyn_array_cnt].blah = einintegerwert; dyn_array_cnt++;
(das obige ist nur ein sinnfreies Beispiel
)
Okay und wenn ich es nun wieder um ein Element vermindern will, mache ich das so:dyn_array_cnt--; free(dyn_array[dyn_array_cnt].ein_string); if(dyn_array_cnt == 0) { free(dyn_array); } else { dyn_array = realloc((test*)dyn_array, dyn_array_cnt * sizeof(test)); }
aber es gibt ja auch die folgende Möglichkeit:
dyn_array_cnt--; free(dyn_array[dyn_array_cnt].ein_string); if(dyn_array_cnt == 0) { free(dyn_array); } else { free(dyn_array+dyn_array_cnt+1); }
Aber welche ist nun richtig bzw. besser? Denn realloc() ist doch extra dafür da, einen Speicherbereich in der Größe zu verändern, egal ob größer oder kleine oder liege ich da falsch??
Denn Speicherlecks zu suchen ist eine üble Angelegenheit; Hat mich bis jetzt das ganze Wochenende gekostet.
Danke & Grüsse,
~code_pilot
-
Du benutzt realloc auch völlig falsch, denn wenn realloc NULL zurückliefert, setzt du einen gütigen Pointer auf NULL. Betrachte dieses Stück Code:
int* ptr; ptr = malloc(sizeof(int)*100); if(!ptr) { /* Fehler! Entsprechend reagieren */ } ... /* die Variable n speichert die neue Größe (egal ob n < 100 oder > 100) der Code funktionert gleich */ ptr = realloc(ptr, sizeof(int)*n);
Dieser Code ist falsch, weil ptr auf NULL gesetzt werden könnte, auch wenn ptr gültig ist. Richtig wäre:
int* ptr, *tmp; ptr = malloc(sizeof(int)*100); if(!ptr) { /* Fehler! Entsprechend reagieren */ } ... /* die Variable n speichert die neue Größe (egal ob n < 100 oder > 100) der Code funktionert gleich */ tmp = realloc(ptr, sizeof(int)*n); if(!tmp) { /* Fehler! Entsprechend reagieren */ /* neue Größe kann nicht allokiert werden */ } if(tmp != ptr) ptr = tmp; /* free(ptr) nicht nötig, weil realloc es bereits getan hat */
Du überprüfst auch sonst nicht, ob malloc/realloc NULL zurückliefern --> schlechtes Styl. Tu das. Und ändere deine realloc Aufrufe und teste deinen Code reneut. Wenn es trotzdem nicht geht, poste mal den neuen Code und dann schau ich mir ihn in Detail.
-
Hallo supertux,
hmm ja da hast du evtl. Recht, aber meine Frage war ja in erster Linie, ob bzw. wie ich das letzte Element meines Array wieder freigebe bzw. ob es so richtig ist.
Meinen Code kann ich leider nicht posten, da es sich um ein kommerzielles 10000 Zeilen Programm handelt, und das Speicherleck ist irgendwo darin versteckt. Gibt es evtl. ein Tool oder eine gute Möglichkeit, Speicherlecks, die durch mit malloc() oder realloc() reservierte Speicherbereiche zu analysieren, denn ich bin mir sicher das ich da irgendwas übersehen habe, wo der Speicher allokiert aber nicht wieder freigegeben wird. Und das ist bei 10000 Zeilen Code fast unmöglich herauszufinden.
Danke & Gruss,
~code_pilot
-
#free(dyn_array+dyn_array_cnt+1);
Das geht so nicht. free, darfst du nur Zeiger übergeben die VORHER von malloc, calloc etc. zurückgegeben wurden. Du kannst nicht mittendrin oder am Ende ein Element freigeben. Nicht wenn du das als zusammenhängenden Speicher allokiert hast, was du ja tust. Mittendrin oder am Ende löschen und freigeben geht nur mit verketten Listen, da dort jedes Element für sich selbst allokiert wurde.
Was an deinem Code funktioniert ist
#free(dyn_array[dyn_array_cnt].ein_string);
Hier übergibst du die Adresse von einem String der durch strdup() erzeugt wurde, strdup() allokiert automatisch Speicher.
-
TheTester schrieb:
Du kannst nicht mittendrin oder am Ende ein Element freigeben. Nicht wenn du das als zusammenhängenden Speicher allokiert hast, was du ja tust. Mittendrin oder am Ende löschen und freigeben geht nur mit verketten Listen, da dort jedes Element für sich selbst allokiert wurde.
Oh alles klar. Dann ist das also bisher nur Zufall, das es funktionierte? Weil das erklärt dann natürlich einiges. Muss ich also immer eine verkettete Liste benutzen, also realloc() nur benutzen, um einen Speicherbereich wie z.B. ein dynamisches char-Array zu vergrößern, nicht um ihn zu verkleinern?
Ich hoffe mal das behebt dann mein Speicherleck, denn so langsam gehen mir echt da die Ideen aus, wo die Ursache liegen könnte.
Danke & Gruss,
code_pilot
-
Ich hab aber gerade im Netz noch was interessantes gefunden:
Interessanter ist da schon die Speicherreservierung die uns die Funktion 'realloc()' bietet. Damit ist es uns nun wirklich möglich im während dem laufenden Programm soviel Speicher zu reservieren wie wir benötigen. Und das für, und vor allem auch, für Arrays.
Mit 'realloc()' sind wir nun in der Lage Dynamische Arrays zu programmieren. Die Anfangsadresse unserem 'dynamischen Array' ist die auf der unser - Zeiger (zgr) zeigt. Der Parameter 'neuegroesse' dient dazu einen bereits zuvor allokierten Speicherplatz auf 'neuegrosse' Bytes zu vergrößern. Apropos, mit 'realloc()' ist es auch möglich den Speicherplatz zu verkleinern. Es wird einfach der hintere Teil freigegeben während der fordere Teil unverändert bleibt. Bei einer Vergrößerung des Speicherplatzes mit 'realloc()' behält der vordere Teil auf jeden Fall seinen Wert und der hintere Teil wird einfach hinten angehängt. Dieser angehängte Wert ist aber wie bei 'malloc()' undefiniert und nicht wie bei 'calloc()' mit 0 initialisiert. Nun ein kleines Beispiel wie man ein Array dynamische machen kann.....Was bedeutet das nun? Soll ich doch besser verkettete Listen einsetzen oder liegt das Problem wo anders?
Gruss,
code_pilot
-
code_pilot schrieb:
also realloc() nur benutzen, um einen Speicherbereich wie z.B. ein dynamisches char-Array zu vergrößern, nicht um ihn zu verkleinern?
doch, verkleinern kannst du auch, das ist kein Problem, sofern du realloc auch richtig benutzt. Was ein Problem angeht, du benutzt malloc/realloc auf eine wilde Art und Weise und kann unter Umstände ins Auge gehen zum Abstürtzen führen. realloc verändert die allokierte Speicherkapazität, egal ob's mehr oder weniger wird.
free(dyn_array+dyn_array_cnt+1); tu das nicht, weil du damit nicht gewehrleistet, dass ein allokierter Pointer übergeben wird. Das ist ein undefiniertes Verhalten und da kann der Compiler machen, was er will. Siehe http://www.fun-soft.de/showtopic.php?threadid=10687&time=1109590273
Ich hab auch nicht gemeint, dass du den ganzen Code posten musst, sondern nur das Stück, das du gepostest hast, nachdem du malloc/realloc Aufrufe verbessert hast, so wie ich es gesagt habe. Und es gibt noch etwas, was mich stört:
if(dyn_array_cnt == 0) { dyn_array = (test*)malloc(sizeof(test*)); }
was ist test und wo wird test deklariert? Denn das liefert 100% nicht, das gewünscht Ergebnis, wie viel Speicher willst du da allokieren?
Dann ist das also bisher nur Zufall, das es funktionierte?
ja
code_pilot schrieb:
Ich hab aber gerade im Netz noch was interessantes gefunden:
Interessanter ist da schon die Speicherreservierung die uns die Funktion 'realloc()' bietet. Damit ist es uns nun wirklich möglich im während dem laufenden Programm soviel Speicher zu reservieren wie wir benötigen. Und das für, und vor allem auch, für Arrays.
Mit 'realloc()' sind wir nun in der Lage Dynamische Arrays zu programmieren. Die Anfangsadresse unserem 'dynamischen Array' ist die auf der unser - Zeiger (zgr) zeigt. Der Parameter 'neuegroesse' dient dazu einen bereits zuvor allokierten Speicherplatz auf 'neuegrosse' Bytes zu vergrößern. Apropos, mit 'realloc()' ist es auch möglich den Speicherplatz zu verkleinern. Es wird einfach der hintere Teil freigegeben während der fordere Teil unverändert bleibt. Bei einer Vergrößerung des Speicherplatzes mit 'realloc()' behält der vordere Teil auf jeden Fall seinen Wert und der hintere Teil wird einfach hinten angehängt. Dieser angehängte Wert ist aber wie bei 'malloc()' undefiniert und nicht wie bei 'calloc()' mit 0 initialisiert. Nun ein kleines Beispiel wie man ein Array dynamische machen kann.....Was bedeutet das nun? Soll ich doch besser verkettete Listen einsetzen oder liegt das Problem wo anders?
Gruss,
code_pilotDas bedeutet, dass realloc dir so viel Speicher gibt, wie du brauchst, weil du ihn praktsich nach beliebig vergößern oder verkleinern kannst. Nur mit "Bei einer Vergrößerung des Speicherplatzes mit 'realloc()' behält der vordere Teil auf jeden Fall seinen Wert und der hintere Teil wird einfach hinten angehängt" kann ich mich nicht anfreunden, weil realloc durchaus auch einen anderen Pointer zurückliefert und deshalb ist es notwendig if(tmp != ptr) wie ich oben gepostet hab.
Mit verkettete Listen würdest du imho wieder auf malloc zurückgreifen müssen, um die Nodes zu erzeugen.
-
supertux schrieb:
code_pilot schrieb:
also realloc() nur benutzen, um einen Speicherbereich wie z.B. ein dynamisches char-Array zu vergrößern, nicht um ihn zu verkleinern?
doch, verkleinern kannst du auch, das ist kein Problem, sofern du realloc auch richtig benutzt. Was ein Problem angeht, du benutzt malloc/realloc auf eine wilde Art und Weise und kann unter Umstände ins Auge gehen zum Abstürtzen führen.
Das mag vielleicht sein, aber es funktioniert ja da ich in die Struktur ja Werte speichern kann - sie sind also nicht NULL.
supertux schrieb:
Und es gibt noch etwas, was mich stört:
if(dyn_array_cnt == 0) { dyn_array = (test*)malloc(sizeof(test*)); }
was ist test und wo wird test deklariert? Denn das liefert 100% nicht, das gewünscht Ergebnis, wie viel Speicher willst du da allokieren?
Oh das stimmt das war mein Fehler, es soll natürlich heißen:
if(dyn_array_cnt == 0) { dyn_array = (test*)malloc(sizeof(test)); }
(habe das vorher net getestet ;))
Definiert ist test ja als struct ganz oben!supertux schrieb:
Mit verkettete Listen würdest du imho wieder auf malloc zurückgreifen müssen, um die Nodes zu erzeugen.
Das ist mir schon klar.
Das Problem ist ganz einfach das ich nicht weis, wo genau der Speicher nicht gefreed wird denn das ist ja das eigentliche Problem. Aber es handelt sich nicht um einen Bug bei malloc/realloc das mein Prog abschmiert, weil verusucht wird, auf einen bereits vergebenen Speicherbereich (das Leck) zuzugreifen?Schade das es dafür keine Tools gibt mit denen man sowas schneller finden kann, ich suche den Fehler schon seid 3 Tagen :(.
Grüße,
code_pilot
-
code_pilot schrieb:
supertux schrieb:
code_pilot schrieb:
also realloc() nur benutzen, um einen Speicherbereich wie z.B. ein dynamisches char-Array zu vergrößern, nicht um ihn zu verkleinern?
doch, verkleinern kannst du auch, das ist kein Problem, sofern du realloc auch richtig benutzt. Was ein Problem angeht, du benutzt malloc/realloc auf eine wilde Art und Weise und kann unter Umstände ins Auge gehen zum Abstürtzen führen.
Das mag vielleicht sein, aber es funktioniert ja da ich in die Struktur ja Werte speichern kann - sie sind also nicht NULL.
Aber es gibt keine Garantie, dass malloc/realloc immer etwas Ungleich NULL zurückliefert. Ja, sie sind meistens nicht NULL, muss aber nicht sein. Deshalb muss mam immer darauf prüfen. Wenn du NULL bekommst und du willst Werte in deine Struktur speichern, dann stürtzt dein Programm ab. Aber deine Sache, ist nicht mein Projekt.
code_pilot schrieb:
if(dyn_array_cnt == 0) { dyn_array = (test*)malloc(sizeof(test)); }
Das gefällt mir ebensowenig. Mich stört das sizeof an diese Stelle.
code_pilot schrieb:
Schade das es dafür keine Tools gibt mit denen man sowas schneller finden kann, ich suche den Fehler schon seid 3 Tagen :(.
Grüße,
code_pilotdoch, ich benutze für solche Fälle immer valgrind
-
supertux schrieb:
doch, ich benutze für solche Fälle immer valgrind
Tja schade das das nicht unter Windoofs läuft.
Hmm ... ich kapier die Welt bald nicht mehr.
Habe jetzt wieder 4 Stunden lang rumgesucht - immernoch dasselbe Problem.Ich erhalte von meinem C++ Builder jedesmal folgende Meldung: "Access violation at address 0041340E. Write of Address 0000383F."
Ich lasse mir jetzt jedesmal den Pointer protokollieren, und das komische ist, das keine meiner malloc()- oder realloc()-Aufrufe einen Speicherbereit bei 0000383F allokiert. Kommt nicht ein mal vor!!! Meine malloc()/realloc() geben immer Adressen in Bereiche wie z.B. 0254505C zurück, und das ist doch wohl ne ganze Ecke weiter. Auch das IBM-Programm Purify (Memory Analyzer & Leak checker) konnte bei mir keine Lecks feststellen ... das kann doch wirklich nicht mehr sein?!!
~code_pilot
-
Ohne richtigen Code können wir dir leider nicht viel helfen. Aber vielleicht kannst du cygwin installaieren, bin sehr sicher, dass valgrind mit cygwin läuft.
-
Es muss ja nicht immer ein Speicherleck sein. Kann auch gut sein das du irgendwo auf einen nicht mehr existenten Pointer zugreifst, oder über eine Arraygrenze drüber hinwegschreibst oder liest, das eine importierte Funktion Unfug macht etc. Könntest vielleicht mal die exe durch einen PE editor schicken und schauen in welchem Segment der Fehler ungefähr auftritt, vielleicht gibt dir das mehr Aufschluss.
-
Hallo TheTester,
yeah das hat gegrooved! Zwar hat mir der PE Editor nicht weitergeholfen, ich habe den Fehler aber "durch zufall" selbst aufgespührt: Habe tatsächlich über einen allokierten Speicherbereich von 4508 Byte hinaus etwas geschrieben, ohne es zu merken, da mein Programm eine Datei mapped, und dabei dann immer ein Byte zu weit übers Ende hinaus schoss.
Daher ist mein Proggy auch "wenn es gerade lust dazu hatte" oder besser gesagt wenn ich mal eine etwas größere Datei in den Speicher lud, immer abgeraucht.
Jetzt gehts - also wirklich mal: So einen verzwickten Bug hatte ich bisher noch nicht!!!
Vor allem weil ich deswegen mein Programm fast komplett umstrukturiert habe... naja egal
(vor allem isses jetzt performanter! ;))
Besten dank an euch nochmal
~code_pilot