AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Tutorials Delphi Buch mit gutem Pointer Kapitel
Tutorial durchsuchen
Ansicht
Themen-Optionen

Buch mit gutem Pointer Kapitel

Ein Tutorial von Aljoscha · begonnen am 20. Nov 2005 · letzter Beitrag vom 20. Nov 2005
Antwort Antwort
Aljoscha
Registriert seit: 30. Dez 2004
Hi!
Wie im Titel beschrieben suche ich ein Buch in dem Pointer abgearbeitet werde.
Ich hatte in der Buchhandlung 2 Bücher in der Hand, doch keins von beiden hatte ein entsprechendes Kapitel
Es sollte vom Einstig bis zu Baumstrukturen beschrieben werden, Google hat mir leider icht weiter geholfen
Thx 4 help
 
Benutzerbild von himitsu
himitsu

 
Delphi 12 Athens
 
#2
  Alt 20. Nov 2005, 20:04
Schau mal bei Luckie rein, dort gibt es ein paar nette Tutorials.

www.luckie-online.de > Delphi

Zu den Pointern hatte ich mal was unter Delphi > Importe gesehn ... einer der dort aufgeführten hatte was dazu.

[add]
zu Beitrag #9 (Luckie) ... ich wußte es doch
  Mit Zitat antworten Zitat
Benutzerbild von mschaefer
mschaefer

 
Delphi 12 Athens
 
#3
  Alt 20. Nov 2005, 20:09
Moin, moin,

Irgendwie kenne ich die Beobachtung. Es gibt viel mehr Bücher über die Oberfläche.

Selbst habe ich die Pointer mit dem Buch "Jetzt lerne ich Delphi" von Markt und Technik vor Jahren gelernt. Es hat etwas über 20 Seiten über Zeiger, wo von Referenzierung, Dereferenzierung, Zuweisungen, handgemachte Speicherreservierung/-freigabe bis zu verketteten Listen, typisierten / untypisierten Zeigern und proceduralen Variablen ein Rundumschlag zu finden ist. Das Werk ist nicht akademisch und liest sich meiner Auffassung nach gut. Hat damals DM 40 gekostet und wird wahrscheinlich inzwischen in der Ramschware zu finden sein.

Grüße // Martin

PS: Allgemein kann man sagen, schau mal nach alten Turbo-Pascal Büchern, denn die sind heute meist günstig.
Martin Schaefer
  Mit Zitat antworten Zitat
Benutzerbild von sniper_w
sniper_w

 
Delphi 6 Enterprise
 
#4
  Alt 20. Nov 2005, 20:11
Pointers ? Na wie wäre es mit "The C Programming Language" von B.W.Kernighan und Dennis M. Ritchie?
Katura Haris
  Mit Zitat antworten Zitat
Nils_13
 
#5
  Alt 20. Nov 2005, 20:26
http://www.rkaiser.de/rkaiser/OPDBuch/opdbuch.html
Und dort: 3.19 Pointer und dynamische Variablen 178
  Mit Zitat antworten Zitat
Aljoscha
 
#6
  Alt 20. Nov 2005, 20:52
Danke für die vielen Antworten
Da ist sicher was dabei
  Mit Zitat antworten Zitat
Nils_13
 
#7
  Alt 20. Nov 2005, 20:54
Schade das Buch von mir bezieht sich auf Delphi1 - Delphi2. Kannst ja vlt. trotzdem was damit anfangen.
  Mit Zitat antworten Zitat
Der_Unwissende
 
#8
  Alt 20. Nov 2005, 21:27
Hi,
leider kann ich dir kein Buch empfehlen, aber ich denke du findest schon hier viel Hilfe zu Pointern. Was möchtest du denn genau wissen (also da du bis Baumstrukturen gehen willst gehe ich davon aus, dass du das grob weißt).

Über Pointer gibt es gar nicht so viel zu sagen (was nichts mit Mächtigkeit oder Schaden zu tun hat). Ein Pointer ist ein Zeiger (ok, geht irgendwie aus dem Namen hervor).
Ein Pointer ist dabei halt ein Zeiger auf eine Speicheradresse. Deshalb ist ein Pointer auch immer ein Prozessorwort groß (also bei 32-Bit CPU u. BS 32, bei 64 Bit dann natürlich 64 Bit etc).
Was aber nun an der Stelle steht, auf die man zeigt, geht nicht aus dem Pointer hervor.
Die Adressen auf die ein Pointer zeigen kann sind immer größer 0. Die Adresse 0 ist reserviert und besonders. Zeigt ein Pointer auf 0 (man sagt auch Nullzeiger *g*), dann zeigt er auf nichts. In Delphi weißt man einem Pointer dazu den wert nil zu (nil = Nullzeiger).
Wenn du nun einen Pointer darauf prüfst, ob er nil ist oder nicht, kannst du so testen ob ihm schon eine Adresse zugewiesen wurde. Wenn er auf nil zeigt, heißt dass, das er auf keine Information zeigt. Also solltest du auch nicht versuchen dort etwas zu lesen, dass führt zu Fehlermeldungen.

Pointer sind zwar sehr mächtig und in sehr Hardwarenahen Sprachen gibt es auch kaum einen Ersatz, aber Pointer werden eigentlich immer gerne gemieden. Der Grund dafür ist einfach, es gibt typisierte Pointer, so genannte Referenzen. Sie machen nicht viel anders als ein Pointer, ausser dass sie auf einen bestimmten Datentypen zeigen.
So zeigen sowohl Pointer als auch Referenzen auf einen Speicherbereich, aber bei einer Referenz kennst du den Datentypen, der sich an dieser Stelle befindet.
Das Problem an einem Pointer ist, dass du ihn leicht auf den falschen Speicherbereich zeigen lässt. Da er nicht prüfen kann was sich an dieser Adresse befindet, kannst du dir also auch Speicher überschreiben, auf den du garnicht zugreifen wolltest.

In Delphi begegnen dir Referenzen implizit immer, wenn du es mit Instanzen von Klassen zu tun hast. Ich würde dir empfehlen Listen und Bäume immer mit Klassen zu realisieren, hat wie ich finde viele Vorteile.

Aber machen wir mit Pointern weiter.
Wie gesagt, immer wenn es um Pointer geht sollte man vorsichtig sein. Trotzdem hat sich sicherlich jmd. was dabei gedacht Pointer einzuführen. Die Grundidee ist recht einfach. Wenn du große Datenstrukturen hast, sagen wir mal nur 40 Byte und du übergibst diese an eine Prozedur und schreibst sie irgendwann in den Speicher zurück, dann müssen jedesmal 40 Byte verschoben werden. Zeigst du hingegen nur auf die Adresse der Datenstruktur, müssen nur 4 Byte (bei einer 32-Bit CPU) verschoben werden. Das ist aber auch die optimale Größe für CPU zugriffe, ein Pointer ist gerade so groß wie ein CPU Register.

Somit sparst du also Zugriffe auf den Speicher, das steigert die Performance.
Eine andere Sache ist es, dass du Variablen in der Regel Call-By-Value übergibst (wie gesagt i.d.R, gibt auch Programmiersprachen wo das anders ist). Was heißt das? Nun ja, wenn du eine Zahl an eine Funktion übergibst und diese in dieser Funktion änderst, dann gilt die Änderung nur für die Funktion. Die eigentlich übergebene Variable behält ihren eigentlichen Wert.
Eine Alternative dazu heißt Call-By-Value-Result. Hier wird die übergebene Variable nach der Abarbeitung der Funktion in den Speicher der Variablen zurück geschrieben. Hier stößt du natürlich auch wieder auf das Problem der großen Datentypen.
Eine andere Möglichkeit ist es natürlich einfach einen Zeiger auf die Adresse der Variablen zu übergeben.
Wenn du nun in den Bereich auf den der Pointer zeigt schreibst, änderst du natürlich auch den Wert, der in der Variablen steht.
Wird sicher deutlicher durch ein Beispiel
Delphi-Quellcode:
// Hier call by Value
procedure CallByValue(Value : Integer);
begin
  inc(Value); // erhöht den Wert von Value um 1
end;

// Hier call by value result
procedure CallByValueResult(var Value : Integer);
begin
  inc(Value); // erhöht den Wert von Value um 1
end;

// Und nun mit einem Pointer
procedure CallWithPointer(p : Pointer);
begin
  inc(Integer(p^));
  // p^ dereferenziert den Pointer
  // Integer(p^) castet / interpretiert den Wert als Integer
  // inc erhöht den Wert um eins
end;

procedure Test;
var V : Integer;
begin
  V := 0;
  CallByValue(V);
  // V = 0
  CallByValueResult(V);
  // V = 1
  CallWithPointer(@V);
  // V = 2
end;
Ok, wie du hier siehst macht verändert der erste Aufruf den Wert von V garnicht. Doch wird der Wert in jeder Funktion um 1 erhöht. Bei CallByValueResult wird der Wert von V um 1 hochgezählt, macht also das was du willst, aber es bleibt das Problem der großen Datentypen. Nun ja, es gibt Compiler die ein Call-by-Value-Result über Referenzen realisieren, aber dass möchte ich nur der Vollständigkeit halber erwähnen (wäre dann natürlich auch Call-By-Reference, aber egal).

Hier ist der letzte Fall interessant. Schauen wir uns erstmal die Funktion CallWithPointer an. Als Parameter übergibst du hier kein Integer sondern einen Pointer. Das heißt p enthält an dieser Stelle wirklich nur eine Adresse. Würdest du jetzt den Wert von p verändern, so veränderst du nur die Adresse auf die der Pointer zeigt. Also Vorsicht!
Wenn du den Wert hinter der Adresse ändern möchtest, musst du den Pointer dereferenzieren. Dass machst du mit dem ^ (hinter dem p). Da der Pointer Typenlos war, weiß Delphi nicht, was genau sich an dieser Adresse befindet. Also musst du noch sagen, was genau da stehen soll. Dazu castest du einfach den Wert an dieser Stelle (hier also in ein Integer). Durch dieses Integer(p^) liest Delphi also 32 Bit (Länge von Integer) ab der Adresse p und interpretiert diese als Vorzeichenbehaftete Zahl.

Schauen wir uns noch den Aufruf an. Während du bei CallByValue und CallByVaueResult einfach die Integer-Variable übergeben kannst, erwartet die letzte Funktion eine Adresse. Die Adresse einer Variablen bekommst du, indem du ein @ vor die Variable schreibst. Soviel zu dem kurzen Programm.

Wie gesagt, Pointer sind an sich untypisiert, aber es gibt auch typisierte Pointer. Ein paar findest du schon vordefiniert in Delphi (ich denke in der Unit System), aber du kannst dir zu jedem Typen einen typisierten Pointer erstellen. Nach Konvention sollten Pointertypen immer mit einem vorangestellten P beginnen. Das macht es auch leichter einen Pointer als solchen zu erkennen.
Bei einem typisierten Pointer ändert sich nicht viel. Der einzigste Unterschied liegt darin, dass Delphi hier eine Adresse auf eine Variable vom richtigen Typ erwartet (ACHTUNG, muss unter Projekt|Optionen|Compiler|Typisierter @-Operator eingeschaltet werden). Ist die Option abgeschaltet, kannst du auch gleich mit untypisierten Pointern arbeiten.
Da sich alles leichter mit einem Beispiel erklären lässt, das gleiche Beispiel mit typisiertem Pointer

Delphi-Quellcode:

type
  PInteger = ^Integer; // definiert einen typisierten Pointer

procedure CallWithPointer(p : PInteger);
begin
  inc(p^); // erhöht den Wert auf den p zeigt um 1
end;

procedure Test;
var V : Integer;
    B : Byte;
begin
  V := 0;
  CallWithPointer(@V);
  // V = 1
  CallWithPointer(@B);
  // Delphi Compiler warnt (wenn Option angeschaltet)
end;
Wie du hier siehst, entfällt einerseits das mit dem Casten, da PInteger ein Zeiger auf ein Integer ist. Gleichzeitig kann nun der Compiler prüfen ob die übergebene Adresse auch auf den richtigen Typ zeigt. Da CallWithPointer nun einen Zeiger auf ein Integer erwartet, wird die Adresse von B nicht akzeptiert. Dies ist auch gut so, immerhin ist ein Byte nun mal nur 1 Byte, ein Integer aber 4 Byte groß.
Würdest du also versuchen an die Adresse von B 4 Byte zu schreiben, dann überschreibst du dir gleich 3 Byte (ohne zu wissen was dort steht). Schon 1 überschriebenes Byte kann dabei schon zu Abstürzen oder nicht auffindbaren Fehlern führen.

Natürlich gibt es noch andere Dinge, die man mit Pointern machen kann. Und nun kommen wir zu den Listen. Bäume sind dabei nur ein spezieller Typ von Liste (ok, eher umgekehrt, aber egal). Jedenfalls ist die Idee dahinter die gleiche.
Was Listen angeht, so fangen wir einfach mal mit den einfachsten an: Einfach Verkettete Listen.

Die Idee einer Liste ist sehr einfach. Es handelt sich um ein dynamische Struktur. Das heißt, die Größe ist nicht von festgelegt. Sie kann wachsen und schrumpfen. Ihr größter Vorteil ist, dass du somit immer nur den Speicher belegst, den du brauchst. Ein Nachteil ist es, dass du nicht weißt wie lang deine Liste ist. Somit kannst du auch nicht einfach auf das Xte Element zugreifen.
Ok, zu Listen findest du definitiv eine Menge (google, hier,...)

Also kommen wir zum einfachen Beispiel und schauen dann warum es funktioniert
Delphi-Quellcode:
type
  PList = ^TList;
  TList = record
    Value : Integer;
    Next : PList;
  end;

var list : PList;

procedure Add(Element : PList);
var buffer : PList;
begin
  // prüfen ob die Liste leer ist
  if list = nil then
    begin
      // wenn sie leer ist, ist das eingefügte element das erste Element
      list := Element;
    end
  else
    begin
      // sonst laufe bis zum letzten Element
      buffer := list;
      while buffer^.next <> nil do
        begin
          buffer := buffer^.next;
        end;
      // füge Element ans Ende ein
      buffer.next := Element;
    end;
end;

function RemoveLast : PList;
begin
  // Wenn die Liste leer ist, wird nil zurück gegeben
  if list = nil then
    begin
      result := nil
    end;
  else
    begin
    // Liste bis zum Ende durchgehen und letztes Element zurückgeben
       while result^.next <> nil do
         begin
           result := result^.next;
         end;
    end;
end;
Ja, hier siehst du einfach mal wie eine solche Liste aussehen kann. TList speichert dabei Information (hier nur ein Integer) und einen Zeiger auf seinen Nachfolger. Zeigt der Nachfolger auf nil, so ist das gleichbedeutend damit, dass es keinen Nachfolger gibt. List speichert den Zeiger auf dein erstes Element. Möchtest du nun auf ein bestimmtes Element zugreifen, so musst du immer vom ersten Element aus zu dem Nachfolger. Dann zu dessen, ... bis du dort bist wo du hinmöchtest. Wichtig ist es, dass du prüfen musst ob es einen Nachfolger gibt.
Zeigt ein Pointer auf nil und du möchtest auf eine Eigenschaft von diesem Pointer zugreifen, so bekommst du eine AccessViolation.

Ja, dies ist natürlich eine sehr einfache Liste. Wie du siehst fügt man ein, indem man zum Ende der Liste läuft und dann den Zeiger auf den Nachfolger setzt. Das hier das letzte Element entfernt wird, ist nur meiner Faulheit zu verdanken.

Wichtig ist es bei Listen (oder auch Bäumen) den Zusammenhang der Elemente zu beachten. Sollte das erste Element entfernt werden, so solltest du beachten, dass das erste Element im Moment die gesamte Liste beinhaltet. Das zurückgegebene Element ist also ein Element, dass noch auf eine Liste zeigt. Würdest du aber einfach nur den Zeiger auf Next löschen, hättest du zwar das erste Element extrahiert, aber auch die Restliste gelöscht.
Also müsstest du das erste Element zwischen speichern, die Restliste also neue Liste speichern und dann den Zeiger des ersten Elements nil setzen.
Delphi-Quellcode:
function RemoveFirst : PList;
begin
  // Wenn die Liste leer ist, wird nil zurück gegeben
  if list = nil then
    begin
      result := nil
    end;
  else
    begin
       // zwischen speichern des ersten Elements
       // welches aber noch auf die Restliste zeigt
       result := list;
       // alte Liste durch Restliste ersetzen
       list := list^.next;
       // löschen der Restliste vom ersten Element
       result^.next := nil;
    end;
end;
Ja, das nächste Thema sind dann doppelt-verkettete Listen und Bäume. Aber darauf werde ich heute nicht mehr eingehen. Hoffe mal es hilft dir soweit weiter. Zu den anderen Themen findest du wirklich überall ne ganze Menge. Wichtig ist, dass du das hier alles verstanden hast. Dann sind andere Listen und Bäume gar nicht viel schwerer. Du musst nur überlegen wo du dann was einfügst und wie du was entfernst.

Falls noch fragen sind, frag einfach!

Gruß Der Unwissende
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

 
Delphi 2006 Professional
 
#9
  Alt 20. Nov 2005, 21:38
Hier gibt es ein es noch ein gutes Pointer Tutorial: http://www.luckie-online.de/Develope...Importe/Motzi/
Michael
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:05 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz