Autor |
Beitrag |
Peter18
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: Mi 21.01.15 17:29
Ein freundliches Hallo an alle,
in der Hilfe steht, dass man mit Pointern auch rechnen kann. Doch mir gelingt es nicht! Immer wenn ich einen Pointer auf etwas zeigen lasse, z.B. eine Struktur von einem Systemaufruf und möchte einen neuen Pointer mit Offset in die Struktur zeigen lassen, kommt eine Fehlermeldung.
Beispiel:
Eine Systemnachricht hat einen Kopf, aus dem hervorgeht, wie der Rest der Nachricht zu interpretieren ist. Auf Grund der Information aus dem Header möchte ich einen Pointer der passenden Struktur auf diesen Teil setzen.
Also so etwas: PInfo := PMessage + SizeOf( Header );
Hat jemand ein Syntax-Beispiel???
Grüße von der Nordsee
Peter
|
|
uall@ogc
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Mi 21.01.15 21:16
Delphi-Quelltext 1: 2: 3: 4: 5:
| PInfo := Pointer(Integer(PMessage) + SizeOf(Header))
oder
PInfo := PInfo(Integer(PMessage) + SizeOf(Header)) |
_________________ wer andern eine grube gräbt hat ein grubengrabgerät
- oder einfach zu viel zeit
|
|
Tastaro
Beiträge: 414
Erhaltene Danke: 23
|
Verfasst: Do 22.01.15 09:58
|
|
Peter18
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: Do 22.01.15 12:27
Hallo uall@ogc, hallo Tastaro,
Dank euch für eure Antworten. Niklaus Wirth sei's gedankt, dass solche Sachen nicht so einfach sind wie in "C". Doch viele seiner Gedanken sind bereits aufgeweicht worden (Exit, Goto, Peek, Poke u.s.w.).
Die Krücke der Typumwandlung habe ich auch schon gefunden. Doch die Hilfe gaukelt die Möglichkeit der direkten Pointerarithmetik vor:
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24:
| Die relationalen Operatoren <, >, <= und >= können Operanden vom Typ PChar haben (siehe den Abschnitt Relationale Operatoren). Bei den folgenden Operatoren können die Operanden auch Zeiger sein. Ausführlichere Informationen über Zeiger finden Sie im Abschnitt Zeiger und Zeigertypen.
Operator Operation Operandtyp Ergebnistyp Beispiel + Zeiger-Addition Zeichenzeiger, Integer Zeichenzeiger P + I - Zeiger-Subtraktion Zeichenzeiger, Integer Zeichenzeiger, Integer P - Q ^ Zeiger-Dereferenzierung Zeiger Basistyp von Zeiger P^ = Gleichheit Zeiger Boolean P = Q <> Ungleichheit Zeiger Boolean P <> Q Der Operator ^ dereferenziert einen Zeiger. Sein Operand kann ein Zeiger auf einen beliebigen Typ mit Ausnahme des generischen Typs Pointer sein, der vor der Dereferenzierung umgewandelt werden muß. P = Q ist nur dann True, wenn P und Q auf dieselbe Adresse zeigen. Mit den Operatoren + und – kann der Offset eines Zeichenzeigers erhöht oder erniedrigt werden. Mit dem Operator – können Sie außerdem den Unterschied zwischen den Offsets zweier Zeichenzeiger berechnen. Für Zeiger-Operatoren gelten die folgenden Regeln:
Wenn I vom Typ Integer und P ein Zeichenzeiger ist, addiert P + I den Wert I zu der von P angegebenen Adresse. Es wird also ein Zeiger auf die Adresse zurückgegeben, die I Zeichen hinter P liegt. (Der Ausdruck I + P entspricht P + I.) P – I subtrahiert I von der Adresse, die von P angegeben wird. Das Ergebnis ist ein Zeiger auf die Adresse, die I Zeichen vor P liegt. Wenn P und Q Zeichenzeiger sind, berechnet P – Q die Differenz zwischen der von P angegebenen (höheren) und der von Q angegebenen (niedrigeren) Adresse. Es wird also ein Integer-Wert für die Anzahl der Zeichen zwischen P und Q zurückgegeben. Das Ergebnis von P + Q ist nicht definiert. |
Daher die Frage geht es auch ähnlich wie in "C"??
Grüße von der Nordsee
Peter
|
|
OlafSt
Beiträge: 486
Erhaltene Danke: 99
Win7, Win81, Win10
Tokyo, VS2017
|
Verfasst: Do 22.01.15 13:36
Delphi beherrscht Pointer-Artihmetik. Wurde ganz groß gefeiert, aber wenige Wochen später war das ganze geraffel mit Pointern sowas von out, das es gar nicht mehr richtig zum Zuge kam.
Wie der von dir zitierte Abschnitt schon sagt: P+I gibt einen Pointer zurück, der um I Stellen "nach hinten" verschoben ist. Allerdings ist Delphi da etwas cleverer als C. Wenn der Compiler weiß, was für ein Pointer das ist, dann ermittelt er von selbst die Größe der dahinterliegenden Datenstruktur und addiert dann I*Datenstrukturgröße dazu. Von allein, ohne das irgendwer was dazu tun muß.
Ergo: Laß P einen PInteger sein. Dann wird P:=P+1 den Pointer 4 Bytes weiterschieben. Nicht ein Byte, denn ein Integer ist 4 Bytes groß. Da du einen PMessage-Pointertyp hast (und ich nehme einfach mal an, der TMessage-Record dahinter ist 42 Bytes groß), dann ergibt PMessage+1 einen Pointer, der 42 Bytes nach hinten verschoben ist. Denn Delphi weiß, das PMessage einfach nur ein ^TMessage ist, und TMessage ist eine Datenstruktur von 42 Bytes Größe.
Willst du es wie in C haben, muß der Pointer in ein PByte gecastet werden. dann geht Delphi von einem Byte-Pointer aus (dahinterliegenedn Datenstruktur = 1 Byte groß) und dann kann man auch den C-Code 1:1 portieren. PChar geht seit D2009 nicht mehr, denn PChars sind seitdem PWideChars, ergo 2 Bytes groß.
_________________ Lies, was da steht. Denk dann drüber nach. Dann erst fragen.
|
|
Peter18
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: Do 22.01.15 16:53
Hallo OlafSt,
danke für Deine Antwort! Genau so hatte ich es erwartet aber is nich.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| var S : String; ps : Pointer;
begin PS := @S; PS := PS+1; |
liefert die Fehlermeldung: "Opperator ist auf diesen Opperandentyp nicht anwendbar" (Zeile 7)
Übrigens "C" ist genauso schlau: "P++" wird abhängig vom Variablentype incrementiert (Kernighan & Ritchie).
Meine Frage ist, worauf ist die Pointerarithmetik anwendbar? Z.B. Integerarrays oder ist da ein Syntaxfehler drin? Egal ob ich String oder Integer verwende, das Ergebnis ist immer das gleiche. Liegt es vielleicht am Typ "Pointer"?
Zitat: | Da du einen PMessage-Pointertyp hast (und ich nehme einfach mal an, der TMessage-Record dahinter ist 42 Bytes groß), dann ergibt PMessage+1 einen Pointer, der 42 Bytes nach hinten verschoben ist. Denn Delphi weiß, das PMessage einfach nur ein ^TMessage ist, und TMessage ist eine Datenstruktur von 42 Bytes Größe. |
Stimmt leider nicht, dann hätte ich die Frage nicht gestellt. Es gibt die Fehlermeldung: "Opperator ist auf diesen Opperandentyp nicht anwendbar"
Grüße von der Nordsee
Peter
|
|
baumina
Beiträge: 305
Erhaltene Danke: 61
Win 7
Delphi 10.2 Tokyo Enterprise
|
Verfasst: Do 22.01.15 17:03
So wie ich das oben lese, solltest du auch erst aus p ein Integer machen, dann 1 dazuzählen und dann den Kram wieder in den Pointer umwandeln.
Delphi-Quelltext 1:
| PS := Pointer(Integer(PS) + 1); |
|
|
WasWeißDennIch
Beiträge: 653
Erhaltene Danke: 160
|
Verfasst: Do 22.01.15 17:53
Pointer ist nunmal ein Zeiger auf irgendwas, da kann der Compiler die dahinterliegende Größe ja nicht kennen. Anders sieht das bei typisierten Pointern aus.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| var S : String; ps : PChar; begin S := 'Hallo Welt'; PS := @S[1]; PS := PS + 1; ShowMessage(PS^); end; |
|
|
Peter18
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: Fr 23.01.15 11:51
Hallo baumina,
Dank für Deine Antwort aber siehe oben.
Hallo WasWeißDennIch,
Dank auch Dir. Dein Beispiel hat funktioniert. Das heißt also die direkte Pointerarithmetik ist nur auf Arrays anwendbar. Sehe ich das richtig??
Grüße von der nebligen Nordsee
Peter
|
|
WasWeißDennIch
Beiträge: 653
Erhaltene Danke: 160
|
Verfasst: Fr 23.01.15 12:57
Das ist eine Frage der Interpretation. Die Daten müssen schon en block im Speicher liegen (wie eben bei Arrays), wenn man sie durchiterieren möchte, was will man auch sonst Sinnvolles auslesen? Außerdem muss dem Compiler die Größe des einzelnen Blocks bekannt sein, was nur bei typisierten Zeigern der Fall ist, der Typ Pointer zählt eben nicht dazu.
|
|
Peter18
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: Fr 23.01.15 18:13
Hallo WasWeißDennIch,
Die Frage ist aufgetaucht, als ich mit Systemschnittstellen hantiert hsbe. Da kommr ein Block mit ainer variablen Struktur mitten drinn und danach weitere Daten. Das Problem kann demnach nur mit der Krücke "Pointer ==> Integer ==> Addition ==> Pointer" gelöst werden um auf sie folgenden Daten zu zeigen. Schade! Es war aber auch eine prinzipielle Frage.
Grüße von der Nordsee
Peter
|
|
SMO
Beiträge: 120
Erhaltene Danke: 18
D2005 Personal
|
Verfasst: Mo 16.02.15 19:22
uall@ogc hat folgendes geschrieben : | Delphi-Quelltext 1:
| PInfo := PInfo(Integer(PMessage) + SizeOf(Header)) | |
Nein, bitte Pointer nicht auf Integer casten! Das funktioniert so zwar problemlos auf 32-Bit-Plattformen, aber das sollte man sich nicht angewöhnen, selbst wenn man nicht vor hat, jemals für 64 Bit zu programmieren.
Korrekter, weil plattformunabhängig, wäre:
Delphi-Quelltext 1:
| PInfo := PInfo(UIntPtr(PMessage) + SizeOf(Header)) |
Statt "UIntPtr" könnte man auch NativeUInt nehmen, aber UIntPtr ist kürzer und ich glaube existierte auch schon bevor NativeUInt eingeführt wurden.
OlafSt hat folgendes geschrieben : | Allerdings ist Delphi da etwas cleverer als C. Wenn der Compiler weiß, was für ein Pointer das ist, dann ermittelt er von selbst die Größe der dahinterliegenden Datenstruktur und addiert dann I*Datenstrukturgröße dazu. Von allein, ohne das irgendwer was dazu tun muß. |
Äh, das macht C genauso, da hat Peter18 recht. Definierst du "int *ptr" und machst dann "ptr++;", so wird der Zeiger um 4 Bytes (=sizeof(int)) bewegt und nicht um 1 Byte.
Peter18 hat folgendes geschrieben : | Niklaus Wirth sei's gedankt, dass solche Sachen nicht so einfach sind wie in "C". Doch viele seiner Gedanken sind bereits aufgeweicht worden (Exit, Goto, Peek, Poke u.s.w.). |
Niklaus Wirth hat mit einem modernen Delphi nicht mehr viel zu tun. Das betrifft nicht nur "Exit" usw. sondern eben auch Pointer. Pointerarithmetik geht wunderbar, unter diesen Voraussetzungen:
1. Man begnügt sich mit Inc und Dec.
2. Man hat Delphi 2009 oder neuer:
2. a. Der Pointer-Typ wurde mit der Direktive {$POINTERMATH ON} compiliert
2. b. oder man aktiviert diese Direktive im eigenen Code vor der Nutzung des Pointers.
Die Standardtypen PByte, PAnsiChar, und PChar haben {$POINTERMATH ON}. Vielleicht unterstützen sie auch schon Pointerarithmetik in älteren Delphi-Versionen, das weiß ich nicht mehr (ich glaube PChar konnte es schon länger, aber das sollte man wenn möglich nur für echte Chars benutzen).
PInteger usw. hat standardmäßig keine Pointerarithmetik, aber sie kann aktiviert werden. Das bedeutet:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
| var IntArray: array [0..3] of Integer = (0, 1, 2, 3); P: PInteger;
begin P := @IntArray[0]; P := P + 2; Inc(P, 2); end;
{$POINTERMATH ON}
begin P := @IntArray[0]; P := P + 2; P[-2] := 10; end; |
|
|
|