Forum: PC-Programmierung C - Fehler bei realloc - Verkettetestruktur erstellen


von max .. (vbc2011)


Lesenswert?

Hallo,

ich möchte aus einem buffer dessen inhalt 99k groß ist eine verkettete 
liste erstellen. Je nach inhalt des buffers werden listenelemente neu 
erstellt und an die liste angefügt - d.h. mit realloc() wird der 
speicher der liste erweitert und an die entsprechende stelle wird das 
neue element angefüt. das ganze funktioniert auch super bis zu einer 
größe von ca. 40k. Habe ich mehr daten - so bekomme ich einen 
heap-fehler - (in dbgheap.c linie 1692) - ich programmiere in C in 
VisualStudio!

Leider bringt mir die abfrage ob mein Retrunpointer der realloc-funktion 
NULL ist auch nichts - da er einfach "leer" - d.h. ich kann nicht darauf 
zugreifen...

Hat jemand zumindest einen Ansatz woran das liegen könnte??
Danke

von nocheinGast (Gast)


Lesenswert?

Was heißt
max .. schrieb:
> mit realloc() wird der
> speicher der liste erweitert und an die entsprechende stelle wird das
> neue element angefüt.

Das ist doch dann keine verkettete Liste. Der Witz an dadran ist doch, 
dass man ohne viel Neuallokieren die Liste erweitern kann. Du erstellst 
dir am besten eine Struktur, in der du die Daten ablegst und einen 
Zeiger auf ein weiteres Element der Struktur.

von max .. (vbc2011)


Lesenswert?

es geht nicht um die liste...
vereinfacht gesagt - ich möchte einfach NUR speicher erweitern - mit 
realloc - ab einer bestimmten größe geht das nicht mehr und ich bekomme 
keinen nullpointer zurück - sondern einen heap-error vom visualstudio.

von Peter II (Gast)


Lesenswert?

max .. schrieb:
> vereinfacht gesagt - ich möchte einfach NUR speicher erweitern - mit
> realloc - ab einer bestimmten größe geht das nicht mehr und ich bekomme
> keinen nullpointer zurück - sondern einen heap-error vom visualstudio.

dann ist der fehler aber woanders, du überschreibst vermutlich irgendwo 
die HEAP-Struktur. Beim nächsten realloc kommt dann dieser Fehler.

von max .. (vbc2011)


Lesenswert?

was heißt das konkret "heap-struktur" überschreiben??

von Peter II (Gast)


Lesenswert?

max .. schrieb:
> was heißt das konkret "heap-struktur" überschreiben??

du schreibst etwas in einen speicher Bereich der dich nichts angeht.

z.b.:
1
int x[5];
2
x[12] = 12;

von max .. (vbc2011)


Lesenswert?

wie kommt es dazu??
und wie kann ich das verhindern??

von Simon B. (nomis)


Lesenswert?

max .. schrieb:
> ich möchte aus einem buffer dessen inhalt 99k groß ist eine verkettete
> liste erstellen. Je nach inhalt des buffers werden listenelemente neu
> erstellt und an die liste angefügt - d.h. mit realloc() wird der
> speicher der liste erweitert und an die entsprechende stelle wird das
> neue element angefüt.

Sprichst Du hier von einem Array oder einer Liste? Wenn Du in diesem 
Speicherbereich eine echte Liste speicherst - also mit Pointern auf z.B. 
vorhergehendes oder folgendes Element in den jeweiligen Listenelementen 
- dann kriegst Du ein Problem, wenn das realloc den Speicherbereich 
durch die Gegend schiebt, weil dann die ganzen Pointer in der Liste 
nicht mehr stimmen.

Wenn Du allerdings nur auf ein Array zugreifst - also Pointer auf den 
Speicherbereich plus index - dann sollte das eigentlich gehen.

Viele Grüße,
         Simon

von Peter II (Gast)


Lesenswert?

max .. schrieb:
> wie kommt es dazu??
> und wie kann ich das verhindern??

java verwenden.


In C muss man halt wissen was man macht, es gibt keine Möglichkeit es zu 
verhindern.

Ich glaube es wird langsam zeit das du uns etwas quellcode zeigst. Deine 
Beschreibung klingt zumindest sehr nach "denn sie wissen nicht was sie 
tun"

von max .. (vbc2011)


Lesenswert?

es handelt sich um ein array!

/* Childnode-Array um 1 vergroessern : */
 aktunode->childnodes = (NODE **) 
realloc(aktunode->childnodes,sizeof(NODEPTR)*(aktunode->index+1));

von Peter II (Gast)


Lesenswert?

max .. schrieb:
> es handelt sich um ein array!
>
> /* Childnode-Array um 1 vergroessern : */
>  aktunode->childnodes = (NODE **)
> realloc(aktunode->childnodes,sizeof(NODEPTR)*(aktunode->index+1));

der Fehler liegt in dem Teil welchen du uns nicht zeigst.

von max .. (vbc2011)


Lesenswert?

du meinst in der node-struktur!?
der code wäre etwas lang um ihn hier zu posten...

das ist mein struktur - welche an der stelle childnodes immer erweitern 
wird.

Programmablauf ist so:
Buffer durchsuchen - wenn alle infos gefunden - struktur um ein element 
erweitern und infos reinschreiben...mehr passiert da nicht.

typedef struct nodestruct
{
  char                   info[ INFO_SIZE];
  char                   name[ NAME_SIZE];
  char                   wert[ WERT_SIZE];
  struct nodestruct  *parentnode;
  struct nodestruct **childnodes; // Array of nodes
  int                    childcount;
  int                    nodelevel;
}
NODE;

typedef NODE  *NODEPTR;

von Simon B. (nomis)


Lesenswert?

max .. schrieb:
> das ist mein struktur - welche an der stelle childnodes immer erweitern
> wird.
>
> Programmablauf ist so:
> Buffer durchsuchen - wenn alle infos gefunden - struktur um ein element
> erweitern und infos reinschreiben...mehr passiert da nicht.
>
> typedef struct nodestruct
> {
[...]
>   struct nodestruct  *parentnode;
>   struct nodestruct **childnodes; // Array of nodes
[...]
> }
> NODE;

Da sind doch deine Pointer.

Können die childnodes selber wieder der parent-nodes für andere sein? 
Falls ja, hast Du ein Problem, dann musst Du nämlich bei einem realloc 
alle *parentnodes ändern, die in dein reallokiertes Array zeigen. 
Realloc kann den Speicherbereich verschieben, damit werden Pointer die 
da reinzeigen potentiell ungültig (!).

Meine Empfehlung: Allokiere alle nodes einzeln und ersetze das 
childnode-Array z.B. durch einen next-sibling-Pointer, verwende also 
eine klassische linere Liste statt dem Array. Dann ändern sich die 
Adressen der Nodes nicht.

Viele Grüße,
        Simon

von max .. (vbc2011)


Lesenswert?

ja genau so ist es - die die childs können auch parents sein!
guter hinweis - dem werde ich mal nachgehen!!

aber warum funktioniert das ganze dann nur für eine bestimmte anzahl an 
nodes - wenn ich diese anzahl überschreite (also mehr als 40k habe) 
gehts nicht mehr?! das ist also kein prinzipielles problem..?!?!

von Simon B. (nomis)


Lesenswert?

max .. schrieb:
> aber warum funktioniert das ganze dann nur für eine bestimmte anzahl an
> nodes - wenn ich diese anzahl überschreite (also mehr als 40k habe)
> gehts nicht mehr?! das ist also kein prinzipielles problem..?!?!

Die libc (malloc/calloc/realloc) allokiert den Speicher beim 
Betriebssystem üblicherweise in größeren Blöcken. Diese Blöcke werden 
dann libc-intern bei einem *alloc aufgeteilt und verwaltet. Das wird so 
gemacht, weil Programme gerne sehr kleine Häppchen haben wollen, das 
"echte" allokieren beim Betriebssystem aber eine teure Operation ist.

Bei Deinem realloc ist es also am Anfang wahrscheinlich, dass der 
Speicher noch nicht verschoben wird, weil hinter dem Block noch von der 
libc allokierter Speicher bereitsteht. Irgendwann aber ist dieser 
"reserve-Platz" aufgebraucht und die libc muss neuen Speicher beim 
Betriebssystem anfragen oder in ihren anderen reservierten Blöcken nach 
freiem zusammenhängendem Speicher für das wachsende Array suchen. Dann 
ändern sich die Pointer und es knallt.

Viele Grüße,
        Simon

von max .. (vbc2011)


Lesenswert?

ja das klingt logisch!
auf dieser fährte war ich auch schon...man kann im visual studio auch 
einen reservespeicher einstellen.
das problem sollte man ja aber im programm lösen!
Aber soweit so gut - mit deinen inofs kann ich gut was anfangen!!
Danke.

von ... (Gast)


Lesenswert?

Das hängt davon ab, wieviel Platz hinter Deinem ursprünglichen Array 
noch frei ist. Z.B. es sind 40k zusammenhängender Speichr frei. Jetzt 
allokierst Du ein Array mit 20k und erweiterst es anschließend auf 40k, 
dann wird das Array einfach nur vergrößert, die alten Daten bleiben an 
der gleichen Stelle im Speicher wie vorher. Erweitest Du das Array aber 
auf z.B. 60k allokiert die Speicherverwaltung einen komplett neuen Block 
kopiert die alten Daten da rein und gibt den alten Block frei.
Im ersten Fall ist der Pointer nach dem realloc der selbe, im zweiten 
Fall nicht.

von max .. (vbc2011)


Lesenswert?

der speicherbedarf wächst ja an - sagen wir ich reallokiere immer um 10k 
pro aufruf! irgendwann reicht der speicher nicht mehr - kann ich diesen 
"zeitpunkt" abfragen??

von ... (Gast)


Lesenswert?

Simon Budig schrieb:
> "reserve-Platz"

Der ist eigentlich nicht reserviert, sondern nur noch nicht anderweitig 
vergeben. Das Ganze häng auch stark von der Reihenfolge der Aufrufe ab. 
Allokiert man z.B. zwei Arrays und vergrößert dann das erste, führt das 
fast immer zu einer Verschiebung im Speicher. Gibt man das zweite Array 
vor dem realoc des ersten wieder frei, dann meist nicht.

von ... (Gast)


Lesenswert?

max .. schrieb:
> der speicherbedarf wächst ja an - sagen wir ich reallokiere immer um 10k
> pro aufruf! irgendwann reicht der speicher nicht mehr - kann ich diesen
> "zeitpunkt" abfragen??

Eher nicht, das hängt wie gesagt auch davion ab was da sonst noch so im 
Speicher passiert und Du müßtest an die internen Listen der 
Speicherverwaltung rankommen. Machbar ist das, aber das willst Du nicht 
wirklich tun.
Du kannst höchsten feststellen, das es passiert ist in dem Du den 
Pointer vor dem realloc mit dem Pointer nach dem realloc vergleichst.

von max .. (vbc2011)


Lesenswert?

dann könnte ich den pointer doch auch mit NULL vergleichen???
aber genau das geht ja nicht - weil der pointer welcher auf den neuen 
bereich zeigt - "leer" ist (also auch nicht NULL).

von Klaus W. (mfgkw)


Lesenswert?

Ein Rückgabewert von realloc() ist entweder NULL oder nicht NULL.
Ein "leer" habe ich noch nicht gesehen.

von Peter II (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> Ein Rückgabewert von realloc() ist entweder NULL oder nicht NULL.
> Ein "leer" habe ich noch nicht gesehen.

es gibt ja noch nullptr  g 

von martin (Gast)


Lesenswert?

Das wogt  ja hin und her hier.

Wie waers  hiermit:  Du machst  keine realloc bei jedem
neuen Element,  sondern  Du allozierst  10 oder 100 oder 1000 neue 
Elemente.
Spart ne Menge Zeit, und Du fragmentierst Deinen Speicher  nicht so 
doll.
Immerhin reallozierst Du den Speicherbereich  jedesmal um 4 Bytes,
das lohnt nicht wirklich  zu sparen.

Vielleicht verscheindet dann auch der Fehler.

von ... (Gast)


Lesenswert?

max .. schrieb:
> dann könnte ich den pointer doch auch mit NULL vergleichen???
> aber genau das geht ja nicht - weil der pointer welcher auf den neuen
> bereich zeigt - "leer" ist (also auch nicht NULL).

Mit NULL vergleichen solltest Du sowieso. Wenn realloc NULL 
zurückliefert, hast Du gar keinen freien Speicher mehr.
Ich meinte eher sowas:
1
NODE **tP = aktunode->childnodes;
2
aktunode->childnodes = (NODE **)realloc(aktunode->childnodes,sizeof(NODEPTR)*(aktunode->index+1));
3
if(aktunode->childnodes) {
4
  if(aktunode->childnodes == tP) {
5
    // memory not moved, all pointers into old block are still valid
6
  } else {
7
    // memory moved, all pointers into old block are now invalid !
8
  }
9
} else {
10
  // error, no more memory
11
  free(tP);
12
}

von Vlad T. (vlad_tepesch)


Lesenswert?

wie schon erwähnt ist das Problem, dass realloc nicht garantiert, dass 
der ursprüngliche Pointer gültig bleibt.
Der Rückgabewert enthält den neuen Pointer auf den Speicher. Der Inhalt 
wurde vorher kopiert.

Das hat zwei Konsequenzen:
alle Pointer auf die ursprüngliche Struktur sind ungültig und müssen 
ersetzt werden.
Alle Pointer untereinander in deiner Linked-List sind kaputt.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.