Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Pointer-Problem / Stack-Überlauf


blaskito - Di 17.11.09 15:17
Titel: Pointer-Problem / Stack-Überlauf
Hallo,

ich habe mir für eine Dateisuche das Konstrukt gebastelt:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
type
  TFileFoundType = record
    Path: Integer;
    Filename: string[255];
  end;
  TFileFound = array[0..0of TFileFoundType;

und natürlich auch eine Variable deklariert:

Delphi-Quelltext
1:
2:
var
  pFileFound: ^TFileFound;

Nun möchte ich Dateien hinzufügen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure TMF.AddFileArray(Path: Integer; Filename: string);
var
  pTemp:^TFileFound;
  i,Size:Integer;
begin
  Inc(fFileCount);
  Size:=SizeOf(pFileFound[0]);
  GetMem(pTemp,fFileCount*Size);
  if pFileFound<>nil then begin
    for i:=1 to fFileCount-1 do pTemp^[i]:=pFileFound[i];
    FreeMem(pFileFound);
  end;
  pTemp^[fFileCount].Path:=Path;
  pTemp^[fFileCount].Filename:=Filename;
  GetMem(pFileFound,fFileCount*Size);
  for i:=1 to fFileCount do pFileFound^[i]:=pTemp[i];
  FreeMem(pTemp);
end;

In der letzten Zeile erhalte ich beim 11. Durchlauf einen Stack-Überlauf. Verdammte Axt: ich finde den Fehler nicht! Was mache ich falsch? Ich muss gestehen, dass Pointer und dynamische Arrays nicht mein tägliches Brot sind. Also bitte Nachsicht für Blödmannsfehler.

Gruß aus dem Norden
blaskito


HelgeLange - Di 17.11.09 16:35

ich sehe keinen grund, warum Du das hin und her kopieren musst, wenn DU mit dynamischen arrays arbeitest.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
type
  TFileFoundType = record
    Path: Integer;
    Filename: string[255];
  end;
  TFileFound = array of TFileFoundType;

var
  pFileFound: ^TFileFound;

procedure TMF.AddFileArray(Path: Integer; Filename: string);
begin
  if pFileFound = nil then 
    fFileCount := 0;

  Inc(fFileCount);
  SetLength(pFileFound, fFileCount);

  pFileFound^[fFileCount-1].Path:=Path;
  pFileFound^[fFileCount-1].Filename:=Filename;
end;


habs ned gestestet, sollte aber so gehen


blaskito - Di 17.11.09 16:59

Hallo,

danke für den Tip. Leider bekomme ich jetzt zum SetLength die Meldung "Inkompatible Typen". Das könnte vielleicht daran liegen, das pFileFound ja nicht das dynamische Array sondern der Pointer darauf ist. Das Hin- und Herkopieren habe ich so aus einigen Artikeln herausgelesen. Damit sollte vermieden werden, dass durch ein weiteres GetMem ein Datenverlust in dem Produktivarray eintritt. Das habe ich einfach mal so als Fakt hingenommen.

Gibt es irgendwo ein Tutorial zum Thema dyn Arrays?

Gruß aus dem Norden
blaskito


Flamefire - Di 17.11.09 17:48

dein fehler ist leicht zu beheben:

Delphi-Quelltext
1:
for i:=1 to fFileCount do pFileFound^[i]:=pTemp^[i];                    


und mit dem dynamischen array ist es klar:
du musst natürlich die pointerdeklaration wegnehmen:

Delphi-Quelltext
1:
pFileFound: TFileFound;                    


zur erklärung: mit getmem holst du dir zusammenhängenden speicher.
stell dir einfach vor, du hast nen datenblock, in dem deine aktuellen daten stehen.
wenn du also mehr brauchst, muss der datenblock größer werden.
also: alten block sichern, datenblock freigeben und größeren holen, alte daten wiederherstellen, neue daten einfügen

stimmt also

bei dyn arrays setzt du die länge erst zur laufzeit. und intern macht delphi genau das oben beschriebene.

aber kurz:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
SetLength(MyArray,0); //initialisieren-->Kein DatenBlock
//...
//Hinzufügen:
SetLength(MyArray,Length(MyArray)+1); //vergrößern...Delphi macht das mit dem umkopieren intern
MyArray[High(MyArray)]:=...//Ans ende anfügen
//Am ende freigeben (wie mit free mem) !!!
SetLength(MyArray,0); //natürlich erst, wenn du das nicht mehr brauchst


ansonsten wäre für dieses thema entweder ein buffer besser (einfach gesagt, ein array mit unterschiedlichen werten für anzahl der einträge und maximaler eintragsgröße; das spart das ständige vergrößern und somit umkopieren-->schneller)
oder eine verkettete liste (gar kein umkopieren)


blaskito - Di 17.11.09 22:13

Hallo Flamefire,

was ich schon sagte: natürlich ein Blödmannsfehler!
Da ich nicht weiß, wie viele Dateien mich erwarten (es dürften bei fast 30.000 Ordnern sehr viele Dateien werden) macht ein Buffer keinen Sinn. Aber die einfach verkettete Liste natürlich. Denn der Zeitaufwand beim Umkopieren wird wahrscheinlich bei einer großen Anzahl Einträgen ein spürbarer Faktor werden.

Vielen Dank
blaskito


Flamefire - Mi 18.11.09 00:04

ich glaube du hast den buffer falsch verstanden.
im prinzip sieht er so aus:

Delphi-Quelltext
1:
2:
3:
4:
TBuffer=record
size,count=Integer;
Elems=Array of xxx;
end;


jetzt initialisierst du den z.b. mit:

Delphi-Quelltext
1:
2:
3:
buffer.size:=128;
SetLength(buffer.Elems,buffer.size);
buffer.count:=0;


und einfügen geht dann so:

Delphi-Quelltext
1:
2:
3:
4:
5:
if(buffer.count>=buffer.size) then begin
buffer.size:=buffer.size*2;
SetLength(buffer.Elems,buffer.size);
end;
buffer.Elems[buffer.count]:=zzz;


verschiedene Delphi klassen machen das auch so (bzw ähnlich)
-->s. Capacity eigenschaft


blaskito - Fr 20.11.09 08:50

Ah ja,

ich glaube, jetzt habbichs. So erzeuge ich immer zusammenhängende Blöcke. Damit fragmentiere ich das RAM natürlich nicht so stark wie mit einer sich dynamisch bei jedem Eintrag verlängernde Liste. Das war mir bislang völlig unbekannt. Ich werd's mal testen.

Schönen Dank
blaskito