AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Warum ist PChar ^= @String[1]?

Ein Thema von d3g · begonnen am 4. Jan 2003 · letzter Beitrag vom 6. Jan 2003
Antwort Antwort
Seite 1 von 2  1 2      
Benutzerbild von d3g
d3g

Registriert seit: 21. Jun 2002
602 Beiträge
 
#1

Warum ist PChar ^= @String[1]?

  Alt 4. Jan 2003, 22:08
Ich bin gerade dabei meine C-Kenntnisse zu vertiefen und dabei ist mir etwas aufgefallen. Ein PChar entspricht einem char* in C(++), sein Ende wird durch #0 angezeigt. Ein String ist ebenfalls ein Pointer auf ein Char, jedoch sind 8 Byte Daten, unter anderem die Länge des Strings:
Zitat von Kylix 3 Hilfe zu 'Long Strings':
AnsiString, also called a long string, represents a dynamically allocated string whose maximum length is limited only by available memory.
A long-string variable is a pointer occupying four bytes of memory. When the variable is empty--that is, when it contains a zero-length string--the pointer is nil and the string uses no additional storage. When the variable is nonempty, it points a dynamically allocated block of memory that contains the string value. The eight bytes before the location contain a 32-bit length indicator and a 32-bit reference count. This memory is allocated on the heap, but its management is entirely automatic and requires no user code.
Der Pascalstring ist nicht nullterminiert (sonst könnte man ja Pascalstrings auch einfach API-Funktionen übergeben).

Daraus ergibt sich mein Problem @String[1] ist ein char* aber woher soll eine API-Funktion wissen, wie lang der String ist, wenn nicht zufällig im Speicherbereich danach binäre Nullen stehen?

Danke im Voraus,
d3g
-- Crucifixion?
-- Yes.
-- Good. Out of the door, line on the left, one cross each.
  Mit Zitat antworten Zitat
Benutzerbild von sakura
sakura

Registriert seit: 10. Jun 2002
Ort: Unterhaching
11.412 Beiträge
 
Delphi 12 Athens
 
#2
  Alt 4. Jan 2003, 22:33
Wie wärs mit: weil Delphi erkennt, dass ein PChar erwartet wird und ein #0 hinten anhängt?
Daniel Lizbeth
Ich bin nicht zurück, ich tue nur so
  Mit Zitat antworten Zitat
Christian Seehase
(Co-Admin)

Registriert seit: 29. Mai 2002
Ort: Hamburg
11.116 Beiträge
 
Delphi 11 Alexandria
 
#3
  Alt 4. Jan 2003, 22:52
Moin Zusammen,

wenn ein Parameter vom Typ string übergeben wird, dann wird intern ein Aufruf der Prozedure UniqueString ausgeführt, der, wenn ich das eben im CPU Fenster richtig gesehen habe, immer eine #00 ans Ende anfügt.
Tschüss Chris
Die drei Feinde des Programmierers: Sonne, Frischluft und dieses unerträgliche Gebrüll der Vögel.
Der Klügere gibt solange nach bis er der Dumme ist
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#4
  Alt 4. Jan 2003, 23:01
UniqueString wird immer beim Casten von Strings aufgerufen. Allerdings frage ich mich, man kann eine String auch so an eine API übergeben:
Code:
Messagebox(hWnd, pointer(s), 'Titel der Msgbox', 0);
Wie funktioniert denn das jetzt? laut einer Webseite wird hier keine verborgene Prozedur aufgerufen, also auch kein UniqueString und ist infolgedessen die schnellste Lösung einen String in eine nullterminierede Zeichenfolge zu casten.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
jbg

Registriert seit: 12. Jun 2002
3.483 Beiträge
 
Delphi 10.1 Berlin Professional
 
#5
  Alt 5. Jan 2003, 00:50
Ein AnsiString fürht 2 Längeninformationen mit sich. Zum einen in den 8 Bytes vor der Adresse, auf die die Variable zeigt, und zum anderen ist jeder String sowieso Nullterminiert. Wandelt man nun einen String per Typecast in einen PChar mit p := PChar(s) um, so wird geprüft, ob der String leer ist (also die Variable intern NIL ist). Wenn dies der Fall ist, wird aus dem NIL-String ein #0-String.

Zusammengefasst:
PChar('') ergibt #0
und
Pointer('') ergibt nil.
  Mit Zitat antworten Zitat
Benutzerbild von d3g
d3g

Registriert seit: 21. Jun 2002
602 Beiträge
 
#6
  Alt 6. Jan 2003, 13:22
Hallo Leute,
danke für eura Antworten, leider bin ich immer noch nicht schlauer...

Zitat von Sakura:
Wie wärs mit: weil Delphi erkennt, dass ein PChar erwartet wird und ein #0 hinten anhängt?
Ich habe gelesen, dass @String[1] gerade ohne Compiler Magic funktionieren soll...

Zitat von Christian Seehase:
wenn ein Parameter vom Typ string übergeben wird, dann wird intern ein Aufruf der Prozedure UniqueString ausgeführt, der, wenn ich das eben im CPU Fenster richtig gesehen habe, immer eine #00 ans Ende anfügt.
Aber @String[1] ist doch kein String, sondern ein ^Char und damit dürfte auch kein UniqueString() ausgeführt werden .

Zitat von jbg:
Ein AnsiString fürht 2 Längeninformationen mit sich. Zum einen in den 8 Bytes vor der Adresse, auf die die Variable zeigt, und zum anderen ist jeder String sowieso Nullterminiert.
Wieso nullterminiert? Wäre er das, dann könnte ich doch ohne Konvertierung einen String an eine API-Funktion übergeben, da er auf das erste Zeichen des Strings zeigt und somit auch ein PChar wäre.

Irgendwie hab ich das Gefühl auf der Leitung zu stehen...

MfG,
d3g
-- Crucifixion?
-- Yes.
-- Good. Out of the door, line on the left, one cross each.
  Mit Zitat antworten Zitat
Benutzerbild von Motzi
Motzi

Registriert seit: 6. Aug 2002
Ort: Wien
598 Beiträge
 
Delphi XE2 Professional
 
#7
  Alt 6. Jan 2003, 13:57
Die Delphi-Strings sind schon nullterminiert.. das einzige was sie von den PChars unterscheidet sind die 8 Bytes (Länge und Referenzzähler) vor der eigentlichen Zeichenkette, wobei der Pointer, der den String referenziert auf das erste Zeichen der Zeichenkette zeigt (die 8 Bytes liegen also eigentlich hinter der eigentlichen Adresse des Strings), und die Spezialbehandlungen die Delphi intern ausführt. Ein PChar ist ein Pointer auf ein Char, daher ist auch naheliegend, dass @String[1] als PChar angesehen werden kann, schließlich stellt der Ausdruck einen Pointer auf das erste Zeichen der Zeichenkette an..

Aber eine ähnliche Diskussion hat es schon einmal gegeben: http://www.delphipraxis.net/viewtopi...t=uniquestring
Manuel Pöter
  Mit Zitat antworten Zitat
Christian Seehase
(Co-Admin)

Registriert seit: 29. Mai 2002
Ort: Hamburg
11.116 Beiträge
 
Delphi 11 Alexandria
 
#8
  Alt 6. Jan 2003, 14:23
Moin d3g,

wenn Du folgende Deklaration hast (Voraussetzung: Für Strings ist die Standardeinstellung eingestellt, also string entspricht HugeString/LongString):

Delphi-Quellcode:
var
  sBuffer : string;
dann enthält sBuffer die Adresse des ersten Zeichens des Strings, wenn der String nicht leer ist.

Du könntest also z.B. schreiben:

Delphi-Quellcode:
var
  dwSize : DWORD;

dwSize := MAX_COMPUTERNAME_LENGTH+1;
SetLength(sBuffer,MAX_COMPUTERNAME_LENGTH+1);
GetComputerName(PChar(sBuffer),dwSize);

// oder

GetComputerName(@sBuffer[1],dwSize);
Im ersten Falle wird dem Compiler mitgeteilt, dass er den Pointer sBuffer als PChar verwenden soll. Da dieser auf das erste Zeichen des Strings zeigt klappt der Typecast.

Bei der zweiten Variante wird die Adresse des ersten Zeichens des Strings übergeben. Würdest Du nur @sBuffer schreiben, so würdest Du die Adresse des Pointers (!) übergeben, was ja naheliegender Weise nicht das ist, was Du willst.

Gäbe es nicht die Möglichkeit den Typ string von Longstring auf Shortstring zu ändern, könnte man sogar folgendes in Betracht ziehen:

Man importiert die Funktion selber:

function GetComputerName(const AsName : string;const ApdwSize : PDWORD) : LongBool; stdcall; external 'kernel32.dllname 'GetComputerNameA'; und kann dan sogar folgendes schreiben:

Delphi-Quellcode:
  dwSize := MAX_COMPUTERNAME_LENGTH+1;
  SetLength(sBuffer,dwSize);
  GetComputerName(sBuffer,@dwSize);
Das funktioniert dann, weil sBuffer die Adresse des ersten Zeichens enthält.

Ich hab' jetzt allerdings nicht überprüft, ob bei einer solchen Konstruktion der String auch Nullterminiert ist, wenn man ihn an eine Funktion übergibt, die den Text ausgeben soll, und nicht als Buffer benötigt.

Da standardmässig allerdings alle Longstrings nullterminiert sein sollen würde ich mal davon ausgehen.

Nimmst Du eine Shortstring muss das schiefgehen, da hier nicht auf das erste Zeichen des Strings gezeigt wird, sondern auf die Länge des Strings.
Tschüss Chris
Die drei Feinde des Programmierers: Sonne, Frischluft und dieses unerträgliche Gebrüll der Vögel.
Der Klügere gibt solange nach bis er der Dumme ist
  Mit Zitat antworten Zitat
Benutzerbild von d3g
d3g

Registriert seit: 21. Jun 2002
602 Beiträge
 
#9
  Alt 6. Jan 2003, 18:21
Zitat:
Die Delphi-Strings sind schon nullterminiert.. das einzige was sie von den PChars unterscheidet sind die 8 Bytes (Länge und Referenzzähler) vor der eigentlichen Zeichenkette, wobei der Pointer, der den String referenziert auf das erste Zeichen der Zeichenkette zeigt (die 8 Bytes liegen also eigentlich hinter der eigentlichen Adresse des Strings), und die Spezialbehandlungen die Delphi intern ausführt.
Wenn LongStrings nullterminiert sind, warum besteht Delphi dann stur darauf, dass ich einen Typecast zu PChar mache? In diesem Falle ist ein String ein PChar (von den Spezialbehandlungen abgesehen, aber davon gibt es in diesem Fall ja keine), ein Pointer auf einen Char eben, char*.

@Christian:
Ich stelle gar nicht infrage, dass ein String ein Pointer auf ein Zeichen ist. Mein Problem liegt darin, dass ein String, wenn er denn nullteminiert ist, doch eine absolut überflüssige Konstruktion wäre. Man könnte einen String intern genauso wie ein PChar behandeln und die Spezialbehandlungen für PChar implementieren. Wäre ein String nullterminiert, warum dann ein Typecast?

Irgendwie steh ich immer noch auf'm Schlauch...

MfG,
d3g
-- Crucifixion?
-- Yes.
-- Good. Out of the door, line on the left, one cross each.
  Mit Zitat antworten Zitat
Christian Seehase
(Co-Admin)

Registriert seit: 29. Mai 2002
Ort: Hamburg
11.116 Beiträge
 
Delphi 11 Alexandria
 
#10
  Alt 6. Jan 2003, 19:06
Moin d3g,

Der Typ string ist, standardmässig ein Longstring, der
  • vor dem eigentlichen Stringinhalt noch ein vier Byte langes Längenfeld und
  • vor diesem Längenfeld ein vier Byte langes Feld als Referenzzähler enthält.

Diese beiden Felder hat ein PChar nicht.

Ein string ist ein dynamisches Array, was ein PChar auch nicht ist.

BTW: Ein string wird nur deshalb intern nullterminiert, damit man keine Probleme mit dem Aufruf von C Funktionen, wie z.B. denen der Windows API bekommt. Selbst für Assembler ist ein Längefeld praktischer

Stellt man auf Shortstring um (dadurch verändert sich automatisch die Bedeutung des Schlüsselwortes string!), so ist die erste Stelle des strings (string[0]!) ein Längenfeld, erst dann folgt der Inhalt.
Ausserdem ist ein Shortstring nicht nullterminiert.

Wie ich in meinem letzten Beispiel glaubte demonstriert zu haben ( )könnte man sich die Funktionen der Windows API so importieren, dass man einen string als PChar verwenden kann, aber:
Das setzt voraus, dass man nicht auf ShortString umstellt.

Die Art wie Borland die Deklaration der Parametertypen beim Import der Windows API Funktionen durchgeführt hat, ist auch der Grund dafür, dass Delphi stur auf einem Typecast besteht. Für den Compiler ist string kein Pointer, und kann somit nicht als Pointer übergeben werden.

Wie gesagt: Es hindert Dich niemand daran, dieses Problem durch eigenen Import der Funktionen, und entsprechender Vergabe der Parametertypen zu umgehen, aber dann wärest Du auch dafür verantwortlich, dass das nicht zu problemen führt, und, falls das für Dich interessant ist. Jeder andere wird wohl erstmal Probleme bekommen Deine Sourcen zu lesen.
Ich weiss auch zugegebener Massen nicht (ich hab's halt noch nicht ausprobiert), was passiert, wenn Du an so einer Stelle dann mal ein Stringliteral übergibst.

Im Prinzip könnte der Compiler das natürlich auch verwalten, denn er muss ja zu jedem Zeitpunkt "wissen" welcher Datentyp sich nun exakt hinter string verbirgt, aber dies ist nicht so implementiert, dass das vorher beschriebene problemlos funktioniert.

Der Witz am Typ string ist übrigens, dass er aufgrund des Längenbytes nicht die Bufferoverflow Probleme kennt, wie man es ja immer wieder so schön bei den Sicherheitsproblemen diverser Software lesen kann, und, dass er, aufgrund des Referenzzählers eine andere (bessere !?) Verwaltung des erforderlichen Speichers ermöglicht.
Wie schon erwähnt, hat der Längenzähler noch den Vorteil, dass die Stringfunktionen der Intel CPUs mit einem Zähler arbeiten, man also nicht mühsam auf eine Null testen muss, sondern direkt das Längenfeld verwenden kann.
Tschüss Chris
Die drei Feinde des Programmierers: Sonne, Frischluft und dieses unerträgliche Gebrüll der Vögel.
Der Klügere gibt solange nach bis er der Dumme ist
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


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 03:42 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