![]() |
SetLength mein problem
Hi
Wer kann mir mal richtig das verschieben Dimensionieren von Arrays in Delphi beibringen. Irgendwie komme ich mit der Materie nicht zurecht und gute Tutorials gibt es auch nicht. Ich möchte also lernen wie man mit arrays unter Delphi richtig umgeht. Würde mich über ein paar Beispiel mit genauer Beschreibung freuen. Nix copy/paste das bringt mir nichts. Also von Anfang an! Was mich zuerst Interessiert wären Dynamische Arrays in verbindung mit Records. Dimensionieren Redimensionieren New(.. in verbindung mit Arrays ReAllocMem GetMem Copy usw ... Halt die ganze palette welche ein vernünfiges(Stabiles) arbeiten mit arrays ermöglicht. Gruß |
Re: SetLength mein problem
Erstmal für den Unterschied statische / dynamische Arrays:
Delphi-Quellcode:
Ein dynamisches Array dimensionierst und realloziierst du einfach nur mit SetLength() und der neuen Länge. Da ist nix mit ReAllocMem oder ähnlichem.
Type
StaticArray = Array[0..2] Of RecordType; DynamicArray = Array Of RecordType; Mit den Funktionen Low() und High() bekommt man den untersten und oberen Index des Arrays raus - funktioniert mit dynamischen wie auch statischen Arrays. Durch den letzten Fakt ist Code welcher auf diesen Funktionen baut (und nicht fest von einem Index 0 ausgeht) auch immer leicht auf ein statisches Array umstellbar. Length() ermittelt die Anzahl der Elemente in einem Array (statisch wie auch dynamisch). Beachte hierbei den Unterschied zwischen Index und Anzahl der Elemente. Auch Length() ist auf beide Typen von Arrays anwendbar. Noch ein Hinweis zum Schluss: Wenn man ein statisches Array an eine Funktion übergibt, welche ein dynamisches Array als Parameter hat, dann wird das statische auf den Index 0 angepasst. Folgendes Beispiel:
Delphi-Quellcode:
Type
MyArray = Array[5..10] Of Integer; Procedure MySubFunc(Const A: Array Of Integer); Begin // ergibt von 0 bis 5 WriteLn('MySubFunc(): Array from ', Low(A),' to ', High(A)); End; Procedure CallIt; Var lArr : MyArray; Begin // ergibt 5 bis 10 WriteLn('CallIt(): Array from ', Low(lArr),' to ', High(lArr)); MySubFunc(lArr); End; |
Re: SetLength mein problem
Erst mal danke für die ausfühliche Beschreibung.
Zitat:
Was mir auffällt der wechsel vom Statisch zum dynamischen array in einer Function kommt ja einen 2 Dimensionalen array gleich oder sehe ich das verkehrt? Array [0..5, 5..10]. Oder? Könnte man ja dann auch direkt so auslegen. Oder macht das einen unterschied. Ein Beispiel: Neue Frage.
Delphi-Quellcode:
Was ich nun nicht verstehe ist folgendes!
Type
PWindowDescr = ^TWindowDescr; TWindowDescr = Packed record hWnd : DWORD; ProcessID : Pointer; Title : String; Klass : String; ExeName : String; End; Var DynamicArray : array of PWindowDescr; Wenn ich nun ein DynamicArray in einer Function einsetze ..
Delphi-Quellcode:
Warum kann ich dann nicht einfach bei SetLength ..
Function GetWindowList(DynamicArray : array of PWindowDescr): DWORD;
Delphi-Quellcode:
eingeben ?
SetLength(DynamicArray, 100)
Theoretisch müßte doch dann das Array um 100 einträge Redimensioniert werden. Woran liegt es nun das es auf diese weise nicht geht. Der Fehler welcher angezeigt wird ist 'incompatible types' Es ist aber doch ein dynamisches Array. Ich habe festgestellt das dies nur geht wenn man eine extra Type declariert
Delphi-Quellcode:
und dann mit SetLength(DynamicArray2, 100) eine neue größe zuweist.
DynamicArray2 = array of PWindowDescr;
Warum geht es also nur auf dieser art Ich möchte ja die Länge von DynamicArray ändern und nicht die von DynamicArray2 Das sind so die kleinen Probleme wo ich nicht so recht bescheid weis. Gruß |
Re: SetLength mein problem
Hallo,
Zitat:
im obigen Beispiel wird das statische Array in der Funktion so behandelt, als wäre es ein dynamisches. Der Aufbau der beiden Typen ist ja grundsätzlich der selbe. Zitat:
Zitat:
Wenn Du irgendwo ein "array of Irgenwas" übergibst, ist das immer ein eigener Typ und gänzlich unterschiedlich zu einem anderen "array of irgendwas". Also;
Delphi-Quellcode:
Beide Funktionen verwenden den gleichen Array-Aufbau, aber der Typ (und damit die Signatur der Funktion) ist immer einzigartig. Delphi erschafft quasi implizit einen neuen Typen.
function X(Value: array of Integer): Boolean;
begin // Tu was end; function Y(Value: array of Integer): Boolean; begin // Tu was end; Gruß xaromz |
Re: SetLength mein problem
Zitat:
Ist sicherlich ne Überlegung wert, sich damit zu befassen. |
Re: SetLength mein problem
Hallo,
Zitat:
Gruß xaromz |
Re: SetLength mein problem
Zitat:
|
Re: SetLength mein problem
Zitat:
Ich hoffe das jetzt richtig verstanden zu haben. Hier die änderung.
Delphi-Quellcode:
Es läuft alles einträge werden richtig eingelesen und zugewiesen.
PWindowDescr = ^TWindowDescr;
TWindowDescr = record hWnd : HWND; ProcessID : Pointer; Title : String; Klass : String; ExeName : String; End; AWindowDescr = array of PWindowDescr; Var wDescr : AWindowDescr; // Alle offene Fenster suchen. Function GetWindowList(var wDescr: AWindowDescr): DWORD; Var IntI : Integer; // Zähler füt die Fenster Handle h_wnd : HWND; // Fenster Handle Index : Integer; Begin // Zähler initialisieren IntI := 0; h_wnd := GetTopWindow(0); // Zuweisen von 100 Array Elementen of PWindowDescr // Zum einlesen der Fenster Handle Index := 100; SetLength(wDescr, Succ(index)); New(wDescr[index]); ZeroMemory(wDescr[index], SizeOf(wDescr[index]^)); Repeat // Zähler um 1 erhöhen inc(IntI); // Wenn mehr als High Fenster Handle gefunden If IntI > High(wDescr) Then // Array um 10 erhöhen SetLength(wDescr, High(wDescr) + 10); // Fenster Handle ermitteln h_wnd := GetWindow(h_wnd, GW_HWNDNEXT); // Fenster Handle gefunden If h_wnd <> 0 Then begin New(wDescr[IntI]); ZeroMemory(wDescr[IntI], SizeOf(wDescr[IntI]^)); // Wert zuweisen wDescr[IntI].hWnd := h_wnd; // Window Informationen einlesen GetWindowInfo(wDescr[IntI]); end; // Schleife durchlaufen bis kein Fenster Handle mehr existiert until h_wnd = 0; // Array Redimensionieren Fenster-Handle Count SetLength(wDescr, IntI); // Ergebnis übergeben Result := IntI; End; Wäre nicht schlecht wenn mir jemand fehler meldet wenn ersichtlich ! Gruß |
Re: SetLength mein problem
Zitat:
gruß |
Re: SetLength mein problem
Ich gebe deinen Quellcode mal wieder mit ein paar Anmerkungen als Kommentar
Delphi-Quellcode:
Ich hoffe das hilft und löst nicht wieder Beleidigungen aus...
PWindowDescr = ^TWindowDescr;
TWindowDescr = record hWnd : HWND; ProcessID : Pointer; Title : String; Klass : String; ExeName : String; End; AWindowDescr = array of PWindowDescr; // Warum ein Array von Zeigern auf den Record anstatt eines Arrays of Records? Var wDescr : AWindowDescr; // Alle offene Fenster suchen. Function GetWindowList(var wDescr: AWindowDescr): DWORD; Var IntI : Integer; // Zähler füt die Fenster Handle h_wnd : HWND; // Fenster Handle Index : Integer; Begin // Zähler initialisieren IntI := 0; h_wnd := GetTopWindow(0); // Zuweisen von 100 Array Elementen of PWindowDescr // Zum einlesen der Fenster Handle Index := 100; SetLength(wDescr, Succ(index)); // also werden hier 101 Elemente angelegt, Zugreifbar mit Index 0 bis 100 New(wDescr[index]); // warum holst du dir den Speicher für nur einen Record (den an Index 100)? Was ist der Sinn? ZeroMemory(wDescr[index], SizeOf(wDescr[index]^)); // warum initialisierst du den Record bei Index 100? // Ich glaube der erste Parameter müsste noch ein ^ bekommen, weil sonst // schreibt ZeroMemory die Array-Pointer-Liste mit 0'en voll anstatt des Records Repeat // Zähler um 1 erhöhen // warum? Willst du Index 0 nicht nutzen? Wenn vor dem Until ein Inc(IntI) inc(IntI); // Wenn mehr als High Fenster Handle gefunden If IntI > High(wDescr) Then // Array um 10 erhöhen SetLength(wDescr, High(wDescr) + 10); // High() gibt dir den höchsten Index an, aber nicht die Anzahl der Elemente. SetLength() will die Anzahl der neuen Elemente, also Anzahl der Elemente bisher + 10, sprich: SetLength(wDescr, Length(wDescr) + 10); // Fenster Handle ermitteln h_wnd := GetWindow(h_wnd, GW_HWNDNEXT); // Fenster Handle gefunden If h_wnd <> 0 Then begin New(wDescr[IntI]); // beachte hier: es ist der Index 100 schon initialisiert! ZeroMemory(wDescr[IntI], SizeOf(wDescr[IntI]^)); // siehe oben: 1. Parameter sollte noch ein ^ bekommen // Wert zuweisen wDescr[IntI].hWnd := h_wnd; // Window Informationen einlesen GetWindowInfo(wDescr[IntI]); end; // Schleife durchlaufen bis kein Fenster Handle mehr existiert until h_wnd = 0; // Array Redimensionieren Fenster-Handle Count SetLength(wDescr, IntI); // wenn du oben das mit dem Inc(IntI) änderst, dann hier Succ(IntI) als höchstes Element setzen // Ergebnis übergeben Result := IntI; End; |
Re: SetLength mein problem
Hi
Zitat:
Im Magnetic thread war ich etwas sauer das du mich hast auflaufen lassen. Hättest du direkt gesag das es nicht geht, hätten wir uns anschließende Diskussionen sparen können.. Aber denke das ist erledigt. (zumindest für mich). Ich entschuldige mich aber nocheinmal dafür. Dieser Thread ist auch keine Übersetzung sondern ein kompletter neuanfang. Da ich bemerkt habe das es nichts bringt irgendeinen VB Code übersetzen zu wollen. Zitat:
Delphi-Quellcode:
Was ist genau der unterschied ?
AWindowDescr = array of PWindowDescr;
//Array von Zeigern auf den Record AWindowDescr = array of TWindowDescr; //Array auf den Record Zitat:
Es gab da ein problem wenn ich mit dem Array(0) begonnen habe die Daten zu füllen werde ihn auf 99 setzen muss dann allerdings schauen ob nach allen änderungen wieder ein AV auftritt. Zitat:
Bin aber mittlerweile darauf gekommen das dadurch zuerst der Index(100) mit Daten gefüllt wird. Ist also verkehrt meine vermutung. Zitat:
Welche parameter genau was machen. Zitat:
Habe inc(IntI); nun ans ende der until schleife gesetzt damit wird der Index 0 nun auch benutzt. Zitat:
Wäre auch irgendwie logisch. ? Zitat:
Zitat:
ZeroMemory(wDescr[IntI]^, SizeOf(wDescr[IntI]^)); folgende Fehlermeldung [Pascal Error] Unit2.pas(235): E2017 Pointer type required lasse ich den Zeiger^ weg diese [Pascal Error] Unit2.pas(235): E2010 Incompatible types: 'Pointer' and 'TWindowDescr' Vorher hatte ich bei AWindowDescr = array of PWindowDescr; keine Fehlermeldungen. Wo ist nun der unterschied ? Zitat:
Nach dem durchlauf der Schleife hat doch IntI autmatisch den höchsten index warum muss er dann mit Succ(IntI) explizit zugewiesen werden ? gruß |
Re: SetLength mein problem
Hi!
Zitat:
Zitat:
Zitat:
Zitat:
Beim Zweiten hast du ein Array wo jedes Element direkt ein Record ist. D.h. ein Eintrag in dem Array ist SizeOf(TWindowDescr) gross. Du bekommst hier im Gegensatz zum Ersten nicht nur 4 Bytes der erst auf den Record zeigt sondern du hast direkt hier den Record liegen. Zitat:
Zitat:
![]() Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
|
Re: SetLength mein problem
Zitat:
Wenn ich kein Array von Zeigern auf den Typ des Records verwende benötige ich kein New() mehr da ich den Record direkt zur verfügung habe. Das bedeutet auch ich benötige ebenfalls ZeroMemory nicht mehr. Demnach sieht mein Code nun so aus!
Delphi-Quellcode:
PWindowDescr = ^TWindowDescr;
TWindowDescr = record hWnd : HWND; ProcessID : Pointer; Title : String; Klass : String; ExeName : String; End; AWindowDescr = array of TWindowDescr; Var wDescr : AWindowDescr; // Alle offene Fenster suchen. Function GetWindowList(var wDescr: AWindowDescr): DWORD; Var IntI : Integer; // Zähler füt die Fenster Handle h_wnd : HWND; // Fenster Handle Begin // Zähler initialisieren IntI := 0; h_wnd := GetTopWindow(0); // Zuweisen von 100 Array Elementen of PWindowDescr // Zum einlesen der Fenster Handle SetLength(wDescr, 100); Repeat // Wenn mehr als 100 Fenster Handle gefunden If IntI > High(wDescr) Then // Array um 10 erhöhen SetLength(wDescr, Length(wDescr) + 10); // Fenster Handle ermitteln h_wnd := GetWindow(h_wnd, GW_HWNDNEXT); // Fenster Handle gefunden If h_wnd <> 0 Then begin // Wert zuweisen wDescr[IntI].hWnd := h_wnd; // Window Informationen einlesen GetWindowInfo(@wDescr[IntI]); end; // Zähler um 1 erhöhen inc(IntI); // Schleife durchlaufen bis kein Fenster Handle mehr existiert until h_wnd = 0; // Array Redimensionieren Fenster-Handle Count SetLength(wDescr, succ(IntI)); // Ergebnis übergeben Result := succ(IntI); End; Zitat:
Brauchen tu ich diesen aber auch nicht mehr da ich es geändert habe
Delphi-Quellcode:
SetLength(wDescr, 100);
Zitat:
Da ich nun direkt den Record einlese hat sich das erstmal erledigt aber destotrotz Danke für die Infos kann bei erneuter verwendung dann darauf zurückgreifen (Thread als .PDF abspeichern) feine Sache. :-D Gruß |
Re: SetLength mein problem
Zitat:
Also erstmal solltest du hier wirklich zwischen Arrays und Records unterscheiden, new wirst du tatsächlich nie direkt im Zusammenhang mit einem Array verwenden müssen/sollen/brauchen. Darauf komme ich aber nachher zurück. An sich ist ein Array in fast allen Programmiersprachen vorhanden. An sich hat das nichts mit veraltet oder nicht zu tun, die Struktur ist einfach denkbar einfach und hat (je nach Implementierung) sehr wenig Overhead (keine indirection nötig). Ein Array kannst du dir immer (vereinfacht, wenn auch nicht viel) als ein Stück Speicher vorstellen. Hast du ein Array vom Typ T mit n Elementen, dann würde hier einfach n * sizeOf(T) Speicher reserviert werden (+ 32 Bit für die Größe). Wichtig ist hier aber einfach die Tatsache, dass du nur ein Stück Speicher hast, dass genau die n mal die Struktur T aufnehmen kann und die direkt hintereinander im Speicher liegen (die Ts). Wenn du die Position des ersten Elements im Speicher kennst, dann kannst du genau diese Eigenschaft ausnutzen. Ausgehend von der Position des ersten Elements, liegt das zweite gerade an der Adresse des ersten + sizeOf(T). Würdest du hier + 2*sizeOf(T) addieren, landest du beim dritten Element... Du kannst hier also ausgehend von der Adresse des ersten Elements direkt die Adresse aller anderen Elemente berechnen. Dies macht Arrays einfach sehr perfomant. Der Unterschied zwischen einem statischen Array und einem Dynamischen liegt natürlich in der festen Größe des statischen Arrays, aber hier wären noch weitere Unterschiede zu finden. So wird ein statisches Array aut. aus dem Speicher entsorgt und der Index kann an einer beliebigen Stelle beginnen. Variablen die als dyn. Array deklariert werden speichern nur einen Zeiger auf das Array. Bevor du dein Array nicht mit setLength initialisierst, gibt es hier einfach keinen Speicher, der wirklich zu dem Array gehört. Während der Zeiger bei einer Instanz- oder Globalen-Variable noch initialiert wird (= nil), ist dies innerhalb von Methoden/Funktionen nicht der Fall. Hier könntest du also einen gültigen Zeiger haben, von dem du aber nicht weißt, wo dieser hinzeigt. Ist dein dyn. Array nicht initialisiert, führt jeder Zugriff auf ein Element im Array zu einem zufälligen Speicherzugriff. Beim Lesen wirst du hier nur unerwartete Werte finden, beim Schreiben kann es sein, dass du den Speicher eines anderen Teils deines Programms überschreibst. Nur im besten Fall wirst du das sofort merken, im schlimmsten Fall ist das ein sehr sehr schwer zu lokalisierender Fehler! Deshalb gilt es immer, dyn. Arrays auch zu initialisieren. Die Länge legst du dabei mit setLength fest und erhälst hier den Zeiger auf das erzeugte, Null indexierte Array. Doch auch hier musst du aufpassen. Da du nur einen Zeiger auf das Array hast, musst du selbst dafür sorgen, dass die dahinter liegenden Daten nicht als unerreichbarer Müll im Speicher bleiben. Es ist immer sauber, dyn. Arrays explizit frei zu geben. Dazu rufst du finalize(<ARRAY>) und setLength(<ARRAY>, 0) auf. Damit stellst du sicher, dass der hier allozierte Speicher sofort (an dieser Stelle) frei gegeben wird. Das ist eigentlich schon so ziemlich alles was ich zu Arrays sagen wollte (der Rest wurde ja schon ausführlich erklärt). Dann gibt es noch die Records. Ein Record ist eine einfache Struktur. Auch hier liegen die einzelnen Elemente eingentlich nur direkt hintereinander im Speicher. Du findest hier allerdings häufig auch noch Ausrichtungen im Speicher, aber das ist erstmal egal. Anders als beim Array, kann ein Record sich aus unterschiedlichen Datentypen zusammensetzen. Das Problem an einem Record ist, dass es immer als ganzes durch den Speicher bewegt wird. Möchtest du ein Record an eine Funktion übergeben, so wird hier das komplette Record übergeben. Da sich ein Record aus verschiedenen Datentypen und aus beliebig vielen Variablen zusammensetzen kann, kannst du hier schnell eine relativ große Struktur erzeugen, deren kopieren entsprechend viel Zeit kostet. Deswegen wird in der imperativen Programmierung häufig mit einem Zeiger auf ein Record gearbeitet. Ein Zeiger ist dabei (auf einem 32 Bit System) immer 32 Bit groß. Dies ist völlig unabhängig von der Größe des Records, auf den dieser Zeiger zeigt (Adressen haben diese feste Größe). Wird nun ein solcher Zeiger übergeben, so müssen nur noch 4 Byte kopiert werden (je nach Record kann dies ein deutlich kleinerer Wert als die Recordgröße bedeuten). Bei der Arbeit mit Zeigern gibt es jedoch verschiedene Dinge zu beachten. Auch hier gilt wieder, dass diese unbedingt initialisiert werden müssen. Es kann sonst leicht zu Problemen kommen. Da ein Zeiger nur die Adresse einer Struktur beinhaltet, wird hier nicht mit einer Kopie des referenzierten Datums gearbeitet. Alle Änderungen an dieser Struktur (Belegung des Records) werden direkt im Speicher der Variablen ausgeführt, sind also quasi-global. Möchte man eine dyn. Anzahl von Records erzeugen, so hat man hier die Auswahl zwischen verschiedenen Möglichkeiten. Hier kommt new in Spiel. New reserviert Speicher für eine bestimmte Struktur. New ist dabei einfach typsicher, man übergibt einen Zeiger auf die Struktur und new alloziert Speicher in der Größe der dereferenzierten Struktur. Dieser Speicher muss später entsprechend wieder frei gegeben werden. Mit new lassen sich so dyn. Examplere einer Struktur erzeugen. Wie man diese verwaltet ist wiederum beliebig und unabhängig vom new. Man kann hier ein Array von Zeigern verwalten oder eine TList verwenden (die greift intern wiederum auf Arrays zurück) oder eine andere Lösung wählen. Aber auch zu new gibt es Alternativen. So kann man auch ein dyn. Array vom Typ der Struktur erzeugen. Dabei wird ausreichen Speicher reserviert um n (Länge des Arrays) Exemplare der Struktur direkt aufzunehmen. Hier sorgt das finalize dann dafür, dass der Speicher auch wieder frei gegeben wird. Zudem kannst du auch direkt Speicher (einer bestimmten Größe) allozieren und beliebig verwenden. Hierauf möchte ich gar nicht mehr näher eingehen, denn Typsicherheit sollte immer genutzt werden, wenn dies möglich ist. Gruß Der Unwissende |
Re: SetLength mein problem
@Der_Unwissende
Sehr interessant dein Bericht! Kenn mich schon mit Arrays aus .. allerdings ist die verwaltung von arrays unter VB nicht ein so großes problem wie in Delphi. Hier kommen doch einige Sachen mehr zum Tragen Zeiger, Records, Pointer .. usw Da habe ich mich im normalfall unter vb nicht mit beschäftigen müssen. Deshalb war auch meine bitte . Anfang des Threads mir die verwendung von Arrays unter Delphi einmal genau zu erläutern. Habe das geschriebene meines erachtens bisher verstanden ;) Danke nochmal für die Infos! gruß |
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:35 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