![]() |
Dynamische Arrays "zu Fuß"
Dynamische Arrays sind ja erst ab D4 und höher bekannt. Aber es gibt ja schon unter D3 Stringliste, was ja im Endeffekt auch nur ein dynmaisches Array of String ist. Da ich damit leibäugle mir D3 Professional ("Delphi for Kids" läßt grüßen) zu kaufen und ich des öfteren doch auf dynamische Arrays angewiesen bin, wollte ich mal fragen, wie man dynamische Arrays zu Fuß, also selber erstellt.
|
Re: Dynamische Array "zu Fuß"
Dynamische Arrays funktionieren ja so, dass bei einem Aufruf von SetLength() soviel Speicher alloziert wird, wie das Array benötigt (also Länge des Array * Elementgröße). Der Zugriff auf ein Speicherelement erfolgt mit der Formel Startadressse + Index * Elementgröße. Bei jedem Aufruf von SetLength wird nun das Array an einen anderen Speicherplatz kopiert und der alte Speicher freigegeben. Pack das ganze in eine schöne Klasse und das sollte es gewesen sein.
OK, du musst für jeden Datentyp eine eigene Implementierung machen, aber das dürfte weitgehend mit C&P zu lösen sein. So mal ein Beispiel, wie der Umgang mit diesem Objekt wäre. Die Daten selbst wären über einen Pointer abgespeichert, außerdem muss die Anzahl der Einträge gespeichert werden.
Delphi-Quellcode:
SetLength würde folgendes tun:
//die Klasse heißt TDynArray
myArray := TDynArray.Create(1); //Array mit der Länge erzeugen, im Konstruktor Aufruf von SetLength myArray[0] := myVar; //myVar an Startposition + 0 schreiben myArray.SetLength(2); //jetzt werden die ganzen Daten des Array an eine neue Speicherstelle kopiert myArray.Free; - NeueLänge * Elementgröße Bytes Speicher allozieren - AlteLänge * Elementgröße Bytes ausgehend von DataPointer in neuen Speicher kopieren - DataPointer mit alter Länge freigeben - DataPointer bekommt die Adresse des neuen Speicherbereichs - Fallunterscheidung: bei Länge 0 kein Kopieren nötig. Der Zugriff auf ein Element: - Die Adresse Index * Elementgröße + Datapointer dereferenzieren und in entsprechenden Typ casten So könnte man das lösen. |
Re: Dynamische Array "zu Fuß"
Ich habe mal versucht das irgendwie umzusetzten, nach einen Tipp von tommie-lie, der hat sich aber leider unter die Dusche verdrückt.
Bitte nicht lachen, mit Zeigern habe ich es nicht so.
Delphi-Quellcode:
Ich bekomme allerdings eine AccessViolation.
type
PString = ^String; var StringArray: PString; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); begin GetMem(StringArray, sizeof(StringArray)+1*sizeof(PString)); PString(StringArray)^ := Edit1.Text; end; |
Re: Dynamische Arrays "zu Fuß"
Hallo Luckie,
möchtest Du wirklich dyn. Array verwenden oder machst Du das nur aus interesse ? TList liefert eigentlich schon alles was Du brauchst. Wenn Du auf max. Geschwindigkeit aus bist dann versuche es mit einer verketteten Liste (beispiele finden sich überall). Ich bin noch nie in eine Situation gekommen die ich nur mit einem dyn. Array lösen konnte. Deshalb behaupte ich: !!! Dyn. Array braucht keiner!!! Ich glaube die dyn. Array's wurden nur für Basic-Programmierer in Delpih aufgenommen. |
Re: Dynamische Arrays "zu Fuß"
Zitat:
|
Re: Dynamische Arrays "zu Fuß"
Nun ja, interesse und weil D3 keine dynamischen Arrays kennt.
Mit TList habe ich noch nie gearbeitet. Kennt das D3 denn? Und wie ginge es damit? |
Re: Dynamische Arrays "zu Fuß"
Hallo Luckie
Zitat:
TList ist in der Delphi-Hilfe bestens dokumentiert. |
Re: Dynamische Arrays "zu Fuß"
Ich würds gar nicht so kompliziert machen und auf einen Typ ausrichte.
Hab hier mal was kleines geschrieben, so wie ichs mir vorstelle:
Delphi-Quellcode:
unit DynArray;
interface TDynArray = class private FDataPointer: Pointer; FElementSize: Integer; FDataSize: Integer; function GetValue(Index: Integer): Integer; procedure SetValue(Index: Integer; const Value: Integer); procedure CopyData(Destination: Pointer; Size: Integer); public constructor Create(InitialLength: Integer); destructor Destroy; override; property Value[Index: Integer]: Integer read GetValue write SetValue; procedure SetLength(NewLength: Integer); end; const ElementSize = sizeof(Integer); implementation { TDynArray } procedure TDynArray.CopyData(Destination: Pointer; Size: Integer); begin CopyMemory(Destination, FDataPointer, Size); end; constructor TDynArray.Create(InitialLength: Integer); begin FElementSize := ElementSize; FDataPointer := nil; FDataSize := 0; SetLength(InitialLength); end; destructor TDynArray.Destroy; begin SetLength(0); end; function TDynArray.GetValue(Index: Integer): Integer; begin if Index > Pred(FDataSize) then //irgendeine Exception oder sowas auslösen Result := Integer(Pointer(Integer(FDataPointer) + Index * FElementSize)^); end; procedure TDynArray.SetLength(NewLength: Integer); var NewData: Pointer; begin if NewLength <> 0 then FreeMem(FDataPointer, FDataSize * FElementSize); GetMem(NewData, NewLength * FElementSize); if FDataSize <> 0 then CopyData(NewData, FDataSize * FElementSize); FreeMem(FDataPointer, FDataSize * FElementSize); FDataPointer := NewData; end; procedure TDynArray.SetValue(Index: Integer; const Value: Integer); begin if Index > Pred(FDataSize) then //irgendeine Exception oder sowas auslösen Integer(Pointer(Integer(FDataPointer) + Index * FElementSize)^) := Value; end; end. |
Re: Dynamische Arrays "zu Fuß"
So, nach der Anregung von Herrn Schumann die Lösung mit TList:
Delphi-Quellcode:
Eigentlich auch so in der Hilfe zu finden.
type
StringArray = record MyString: String; end; PStringArray = ^StringArray; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var MyList: Tlist; sa: PStringArray; Loop: Integer; begin MyList := TList.Create; try New(sa); sa.MyString := 'Egon'; MyList.Add(sa); New(sa); sa.MyString := 'Klaus'; MyList.Add(sa); New(sa); sa.MyString := 'Michaela'; MyList.Add(sa); for Loop := 0 to myList.Count-1 do begin sa := MyList.Items[Loop]; Listbox1.Items.Add(sa.MyString); end; for Loop := 0 to MyList.Count-1 do begin sa := MyList.Items[Loop]; Dispose(sa); end; finally FreeAndNil(MyList); end; end; Zu beahcten ist nur, dass man vor dem Hinzufügen, neuen Speicher reserviert (New) für den Record und nach Gebrauch selbigen wieder frei gibt (Dispose). Ist eventuell etwas umständlicher, dafür ist man mit dem Zeiger auf den Record aber felxibeler, man kann mehr Speichern und muss nicht auf mehrdimensionale Arrays zurückgreifen. |
Re: Dynamische Arrays "zu Fuß"
Zitat:
|
Re: Dynamische Arrays "zu Fuß"
Flexibeler in dem Sinne, da ich einen Zeiger auf einen Record habe, kann ich so viele Felder wie ich will in dem Record haben. In dem Sinne hätte man ein mehrdimensionales Array.
|
Re: Dynamische Arrays "zu Fuß"
Ja, aber ein Array of Pointer wäre genau so flexibel.
Abgesehen von den Vor- oder Nachteilen, die eine Klassenimplementierung mit sich bringt, natürlich. |
Re: Dynamische Arrays "zu Fuß"
Zitat:
|
Re: Dynamische Arrays "zu Fuß"
Ein Array ist eine Liste, allerdings eine sequenzielle. Ob der Datentyp Pointer, Integer oder Mohnbrötchen ist, spielt überhaupt keine Rolle. Ein Array mit Datentyp Pointer hat auch nicht mehr (oder weniger) zu tun mit einer verketteten Liste. Dort kannst du ja als Datentyp bei dem Wert Pointer angeben.
|
Re: Dynamische Arrays "zu Fuß"
Verdammt. TList ist in Classes drin. Das kann ich ja nun gar nicht brauchen, wen ich ohne VCL programmiere, das sind gleich wieder 130 KB mehr. :evil:
|
Re: Dynamische Arrays "zu Fuß"
Aber falls du die D3Prof-Version hättest, könntest du den TList Code rauskopieren, und kämst auf unter 130 KB...
|
Re: Dynamische Arrays "zu Fuß"
Zitat:
|
Re: Dynamische Arrays "zu Fuß"
Hallo Luckie,
der Nachteil bei Lösungen, die generisch mit Pointern arbeiten (zB TList) liegt in der fehlenden Fähigkeit, mit Typen zu arbeiten. Der Nachteil von Verketteten Listen liegt in dem relativ hohen Aufwand der Verwaltung. Die Folgende Lösung habe ich zu DOS-Zeiten häufig verwendet:
Delphi-Quellcode:
der Nachteil ist, dass Du das Ausmaß des Arrays nicht unmittelbar über Low und High ermitteln kannst (es muss zusätzlich, zB in einem Record, die Größe festgehalten werden) und die Indexberechnung bei höherdimensionalen Arrays nur dann funktioniert, wenn die "inneren" Dimensionen fest sind.
procedure TForm1.Button1Click(Sender: TObject);
type TMyType = Integer; TMyArray = array[0..MaxInt div SizeOf(TMyType)-1] of TMyType; PMyArray = ^TMyArray; var myArray : PMyArray; mySize : Integer; begin mySize:= 137*42; GetMem(myArray, SizeOf(TMyType)*mySize); try myArray^[0]:= 1; myArray^[1]:= 2; myArray^[2]:= myArray^[0]+myArray^[1]; finally FreeMem(myArray); end; end; Darüber hinaus musst Du (wie bei den anderen Lösungen) die Speicherverwaltung selbst implementieren musst (um Gegensatz zu dynamischen Arrays, bei denen eine transparente Referenzzählung vom Compiler übernommen wird). Da Du ohnehin D6 besitzt, würde ich Dir empfehlen, weiterhin dynamische Arrays zu verwenden. Falls Du mit Klassen arbeitest, könntest Du eine Lösung, die typensicher ist, mit dem ![]() ![]() |
Re: Dynamische Arrays "zu Fuß"
Zitat:
|
Re: Dynamische Arrays "zu Fuß"
Hier eine aehnliche Implementierung von TList (allerdings als verkettete liste) von einem sehr alten projekt von mir:
Delphi-Quellcode:
[edit=Luckie]Wir haben Delphi-Tags. :zwinker: Mfg, Luckie[/edit]
type
{ **************************************************************************** * TList * **************************************************************************** } PListItem = ^TListItem; TListItem = record Data: Pointer; Prev: PListItem; Next: PListItem; end; TList = class private FStart: PListItem; FEnd: PListItem; FCount: Integer; FCurrent: PListItem; public constructor Create; destructor Destroy; override; procedure Clear; procedure Add(Data: Pointer); function Count: Integer; function GetItem(Index: Integer): Pointer; procedure SetItem(Index: Integer; Data: Pointer); function Remove(Data: Pointer): Boolean; function Delete(Index: Integer): Boolean; procedure BeginWalk; procedure BeginWalkEnd; function Walk(var Data: Pointer): Boolean; function WalkBack(var Data: Pointer): Boolean; function GetCurrent(var Data: Pointer): Boolean; function SetCurrent(Data: Pointer): Boolean; function IndexOf(Data: Pointer): Integer; end; { **************************************************************************** * TList * **************************************************************************** } constructor TList.Create; begin inherited Create; FStart := nil; FEnd := nil; FCount := 0; FCurrent := nil; end; destructor TList.Destroy; begin try Clear; finally inherited Destroy; end; end; procedure TList.Clear; var Current: PListItem; Next: PListItem; begin Current := FStart; while Current <> nil do begin Next := Current^.Next; try Dispose(Current); finally Current := Next; end; end; FStart := nil; FEnd := nil; FCount := 0; FCurrent := nil; end; procedure TList.Add(Data: Pointer); var item: PListItem; begin New(item); item^.Data := Data; if (FEnd = nil) or (FStart = nil) then begin item^.Prev := nil; item^.Next := nil; FStart := item; FEnd := item; end else begin FEnd^.Next := item; item^.Next := nil; item^.Prev := FEnd; FEnd := item; end; Inc(FCount); end; function TList.Count: Integer; begin Result := FCount; end; function TList.GetItem(Index: Integer): Pointer; var x: Integer; item: PListItem; begin item := FStart; for x := 1 to Index do begin if item = nil then begin Break; end; item := item^.Next; end; if item = nil then begin Result := nil; end else begin Result := item^.Data; end; end; procedure TList.SetItem(Index: Integer; Data: Pointer); var x: Integer; item: PListItem; begin item := FStart; for x := 1 to Index do begin if item = nil then begin Break; end; item := item^.Next; end; if item <> nil then begin item^.Data := Data; end; end; function TList.Remove(Data: Pointer): Boolean; var item, next: PListItem; begin Result := false; item := FStart; while item <> nil do begin next := item^.Next; if item^.Data = Data then begin if item^.Prev <> nil then begin item^.Prev^.Next := next; if next <> nil then begin next^.Prev := item^.Prev; end else begin FEnd := item^.Prev; item^.Prev^.Next := nil; end; end else begin FStart := next; if next <> nil then begin next^.Prev := nil; end else begin FEnd := item; end; end; try Dec(FCount); if item = FCurrent then begin if FCurrent <> nil then begin FCurrent := FCurrent^.Prev; end; end; Dispose(item); finally Result := true; end; Break; end; item := next; end; end; function TList.Delete(Index: Integer): Boolean; var item, next: PListItem; x: Integer; begin Result := false; item := FStart; x := 0; while (item <> nil) and (x <= Index) do begin next := item^.Next; if x = Index then begin if item^.Prev <> nil then begin item^.Prev^.Next := next; if next <> nil then begin next^.Prev := item^.Prev; end else begin FEnd := item^.Prev; item^.Prev^.Next := nil; end; end else begin FStart := next; if next <> nil then begin next^.Prev := nil; end else begin FEnd := item; end; end; try Dec(FCount); if item = FCurrent then begin if FCurrent <> nil then begin FCurrent := FCurrent^.Prev; end; end; Dispose(item); finally Result := true; end; Break; end; item := next; Inc(x); end; end; procedure TList.BeginWalk; begin FCurrent := FStart; end; procedure TList.BeginWalkEnd; begin FCurrent := FEnd; end; function TList.Walk(var Data: Pointer): Boolean; begin Result := FCurrent <> nil; if Result then begin Data := FCurrent^.Data; FCurrent := FCurrent^.Next; end; end; function TList.WalkBack(var Data: Pointer): Boolean; begin Result := FCurrent <> nil; if Result then begin Data := FCurrent^.Data; FCurrent := FCurrent^.Prev; end; end; function TList.GetCurrent(var Data: Pointer): Boolean; begin Result := FCurrent <> nil; if Result then begin Result := FCurrent^.Prev <> nil; if Result then begin Data := FCurrent^.Prev^.Data; end else begin Data := FCurrent^.Data; end; end; end; function TList.SetCurrent(Data: Pointer): Boolean; begin Result := FCurrent <> nil; if Result then begin Result := FCurrent^.Prev <> nil; if Result then begin FCurrent^.Prev^.Data := Data; end else begin FCurrent^.Data := Data; end; end; end; function TList.IndexOf(Data: Pointer): Integer; var item: PListItem; x: Integer; begin Result := -1; x := 0; item := FStart; while item <> nil do begin if item^.Data = Data then begin Result := x; Break; end; Inc(x); item := item^.Next; end; end; |
Re: Dynamische Arrays "zu Fuß"
Danke dir, aber ich werde mit der TList aus Classes schon klar kommen.
Was macht eigentlich die Methode Walk? |
Re: Dynamische Arrays "zu Fuß"
die kann man verwenden wenn man einfach alle eintraege der TList durchlaufen will bis zum Ende
|
Re: Dynamische Arrays "zu Fuß"
Hm. Dann hätte ich aber BeginWalk und BeginEndWalk private gemacht.
|
Re: Dynamische Arrays "zu Fuß"
nope, sonst kann man sie ja nicht verwenden wenn die klasse in einer anderen unit ist ;)
|
Re: Dynamische Arrays "zu Fuß"
Die Methoden setzten doch nur den Start- und Endpunkt und werden nur von der Klasse für Walk gebraucht. Aber so genau habe ich sie mir nochnicht angekuckt.
|
Re: Dynamische Arrays "zu Fuß"
nein das ist eher so gedacht:
Code:
bzw.
List.BeginWalk
while List.Walk(item) do begin ... end;
Code:
List.BeginWalkEnd
while List.WalkBack(item) do begin ... end; |
Re: Dynamische Arrays "zu Fuß"
Liste der Anhänge anzeigen (Anzahl: 1)
Hab mir vor kurzem auch D3Pro zugelegt und mich mit dynamischen Arrays und einer nonVCL-Alternative zu TStringList beschäftigt und bin auch zu dem Schluss gekommen mich bei der Classes.pas zu bedienen, die ja auch bei D3Pro beiliegt.
Ergebnis siehe Anhang. Anwendungsbeispiel siehe ![]() |
Re: Dynamische Arrays "zu Fuß"
Gut, ich habe es mir etwas einfachher gemacht. Ich musste nur die Exceptions auskommentiren, weil die wiede rin SysUtils drin sind. Mal sehen, was ich mir da einfallen lassen kann.
|
Re: Dynamische Arrays "zu Fuß"
Bei der Pro-Version sind doch die Sourcen dabei, dann kannst du die doch den entsprechenden Source rauskopieren.
Edit: Ups... Habe die 2. Seite übersehen... |
Re: Dynamische Arrays "zu Fuß"
Zitat:
kannst Du mir kurz zeigen, wie Du mit diesem internen Iterator etwas in der Art
Delphi-Quellcode:
hinbekommst?
for i:=0 to High(myArary)-1 do
for a:= i to High(myArray) do doSth(myArray[i], myArray[a]); Darüber hinaus sind solche Zustandsbehaftete interne Interatoren problematisch bei Threads... |
Re: Dynamische Arrays "zu Fuß"
Zitat:
Delphi-Quellcode:
ist zwar nicht 100% das selbe aber es handelt sich wie gesagt um eine verkettete liste und ist deshalb nicht direkt vergleichbar mit einm array.
list.BeginWalk;
while list.Walk(sublist) do begin sublist.BeginWalk; while sublist.Walk(entry) do doSth(sublist, entry); end; end; Zitat:
|
Re: Dynamische Arrays "zu Fuß"
Die SubList muss ich wohl übersehen haben... Und muss man dieses Exemplar dann nicht wieder freigeben?
Ich wollte nicht behaupten, dass Arrays threadsafe sind, aber sie sind, im Gegensatz zu einem zustandsbehafteten internen Iterator, immerhin treadable. Falls Dich das Thema interessiert, sieh Dir mal das ![]() Eine Lösung könnte dann zB so aussehen (wenn die Iteratoren mit einer Referenzzählung ausgestattet sind)
Delphi-Quellcode:
bzw
with myList.Iterator do
while HasNext do DoSth(Next);
Delphi-Quellcode:
with myList.Iterator do
with HasNext do begin myItem:= Next; with myList.Iterator(myItem) do while HasNext do DoSth(myItem, Next); end; |
Re: Dynamische Arrays "zu Fuß"
da ich mich mit win32 kernel mode programmierung relativ gut auskenne, weiss ich auch wie man thread-safe code schreibt ;) Wie gesagt das war nicht das Ziel meiner implementierung damals. Es hatte fuer meine Zwecke gereicht.
|
Re: Dynamische Arrays "zu Fuß"
am besten sind immer noch die doppelt verketteten listen, wenn auch nicht threadsafe...
|
Re: Dynamische Arrays "zu Fuß"
Das kommt immer auf die Anwendung an, ich bin ebenfalls ein Fan von Hashtables und Skiplisten und für suchen in Strukturen natürlich Bäumen...
Bei dem von Luckie skizzierten Anwendungsfall, nämlich einem Ersatz für dynamische Arrays, halte ich aber alles, was über eine Lösung der Art
Delphi-Quellcode:
hinausgeht, für maßlos übertrieben.
myArray^[n]:= MyTypeCheckedVar;
|
Re: Dynamische Arrays "zu Fuß"
Zitat:
Zitat:
|
Re: Dynamische Arrays "zu Fuß"
aber du gibst ja nicht den Quellcode von der TList-Definition weiter, du kompilierst sie ja in dein Prog.
Also hast du doch eigentlich kein Prob mit dem Copyright mfg vbinsider |
Re: Dynamische Arrays "zu Fuß"
Zitat:
|
Re: Dynamische Arrays "zu Fuß"
Zitat:
Wenn man das Copyright nun genau interpretiert, so dürfte das keine Verletzung darstellen. |
Re: Dynamische Arrays "zu Fuß"
Und was solldas bringen, bei OpenSource die Kompilate weiterzugeben? das kan ich mir dann ja auch sparen. Aber davon mal abgesehen, mit der Prof werde ich bestimmt kein OpenSource machen. :wink:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:12 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz