Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Frage zum Aufbau eines Strings (https://www.delphipraxis.net/38227-frage-zum-aufbau-eines-strings.html)

SebE 16. Jan 2005 14:11


Frage zum Aufbau eines Strings
 
Hi,
meine Frage ist folgende:
Warum kann man bei einem String NICHT auf das "0"te Zeichen zugreifen?
Was ist darin gespeichert?

Mein Lehrer sagte die Länge des Strings. -> Wie kann ich diese Auslesen??

Danke für Erklärungen

SirThornberry 16. Jan 2005 14:17

Re: Frage zum Aufbau eines Strings
 
Also die Länge steht da definitiv nicht.
Dann das 0te Element ist ja nur 1 Byte und in einem Byte könnte man maximal eine Länge von 255 Zeichen vermerken, ein String kann aber bis zu 4 GB groß sein. Aber ungefähr hat dein Lehrer recht, es hat was mit der Längenangabe des Strings zu tun. Delphi hat für Strings eine eigene Speicherverwaltung, deswegen kann es auch wunderschön krachen wenn du zwischen DLL und Hauptprogramm mit String arbeitest (ohne ShareMem) weil dann einfach die Delphieigene Speicherverwaltung nicht ganz stimmt..

Kurz und knapp: ich hab keine Ahnung was dort genau drin steht, aber die Länge wird auf dem einen Byte kaum gespeichert sein.

SebE 16. Jan 2005 14:21

Re: Frage zum Aufbau eines Strings
 
Zu den "255" in einem Byte hast du Recht.
Es könnte ja sein, dass das nicht als Zahl, sondern als Zeichen gespeichert wird.

Kurz:
String[0]:=Chr(Länge des Strings)

Ich dachte, ich hätte das ma irgendwo gesehen :gruebel:
vielleicht auch nur geträumt :wink:

mirage228 16. Jan 2005 14:22

Re: Frage zum Aufbau eines Strings
 
Hi,

unter Turbo Pascal war in [0] die Länge von Strings gespeichert.
Ein String war damals noch in etwa ein array[Byte] of Char ;)

Beim Typ "ShortString" von Delphi dürfte das immernoch so sein.

Der neue HugeString ("String") hat seine Länge in 4 Bytes gespeichert, an die Du, genauso wie an den Referenzzähler, nicht ohne Umwege rankommst. Da musst Du immer Length() verwenden.

Nur noch zur Info ;) :
In "String[-8]" steckt der Referenzzähler und in "String[-4]" steckt der Längenzähler.
Ich habe das mal in Anführungszeichen gesetzt, weil Du ja nicht so darauf zugreifen kannst :)

mfG
mirage228

SirThornberry 16. Jan 2005 14:23

Re: Frage zum Aufbau eines Strings
 
ja eben, wenn es mit "chr(Laenge)" gespeichert wird so kann die Länge höchsens 255 sein. Wenn die Zahl größer ist würde mehr benötigt und somit würde das ganze ja in StringVariable[1] weitergehen was nicht der fall ist.

SirThornberry 16. Jan 2005 14:25

Re: Frage zum Aufbau eines Strings
 
Zitat:

Zitat von mirage228
Hi,

Nur noch zur Info ;) :
In Length(String)-8 steckt der Referenzzähler und in Length(String)-4 steckt der Längenzähler.

Ist das jetzt verarsche? Wenn nicht, erklärs mal genauer.

Angenommen mein String ist "abcdefghij", dann wäre length (10) - 8 ja 2. Und MeinString[2] zeigt ja dann wohl auf eine andere stelle als auf den Referenzzähler.

mirage228 16. Jan 2005 14:27

Re: Frage zum Aufbau eines Strings
 
Zitat:

Zitat von SirThornberry
Zitat:

Zitat von mirage228
Hi,

Nur noch zur Info ;) :
In Length(String)-8 steckt der Referenzzähler und in Length(String)-4 steckt der Längenzähler.

Ist das jetzt verarsche? Wenn nicht, erklärs mal genauer.

Angenommen mein String ist "abcdefghij", dann wäre length (10) - 8 ja 2. Und MeinString[2] zeigt ja dann wohl auf eine andere stelle als auf den Referenzzähler.

Ups :oops: ,
hab mich verschieben. Ich meinte an Position -4 und -8 im String sitzen die beiden besagten Werte, also String[-8] und String[-4] in etwa :)

mfG
mirage228

SebE 16. Jan 2005 14:31

Re: Frage zum Aufbau eines Strings
 
Das stößt aber gegen die "unteren Grenzen" eines Strings:

... :=S[-4];

mirage228 16. Jan 2005 14:32

Re: Frage zum Aufbau eines Strings
 
Zitat:

Zitat von SebE
Das stößt aber gegen die "unteren Grenzen" eines Strings:

... :=S[-4];

So geht das natürlich nicht (daher schrieb ich auch "in Etwa"). Man kann nicht direkt auf den Längen- bzw. Referenzzähler zugreifen.

mfG
mirage228

Muetze1 16. Jan 2005 14:33

Re: Frage zum Aufbau eines Strings
 
Moin!

Das stimmt schon - so wurde es früher gemacht. Die als ShortString bekannten Strings arbeiten immernoch so, daher haben diese eine Beschränkung auf maximal 255 Zeichen und im 0. Zeichen ([0]) steht auch tatsächlich die Länge drin. Und für die heutigen AnsiStrings gilt dies nicht mehr, SirThornberry hat ja schon gut erläutert das man in das Byte keine Längenangabe für 2 GB reinbekommt. Da die Strings aber kompatibel sein sollten so dass die Anwender nicht eine Routine für ShortString und eine für AnsiString schreiben müssen, deshalb gibt es [0] bei AnsiStrings auch noch, nur das hier der Compiler bei einem direktem Zugriff abblockt. Zum Beispiel gibt die Hilfe zu SetLength() an, das sie bei ShortStrings nur das Längenbyte [0] setzt und bei anderen Strings Speicher alloziiert.

@SirThornberry: Die Einschränkung mit String und DLL gilt nur für AnsiString, da diese mit internen Referenzen arbeiten - im Gegensatz dazu hat man mit WideStrings oder ShortStrings keine Probleme (keine Referenzen).

MfG
Muetze1

/EDIT: einmal Pizza aus dem Ofen geholt, schon 5 Posts zu spät...

SirThornberry 16. Jan 2005 14:34

Re: Frage zum Aufbau eines Strings
 
ohne zu wissen ob das was mirage228 geschrieben hat richtig ist: Klar ist -4 außerhalb des zulässigen. 0 ist auch außerhalb des zulässigen, trotzdem kann man drauf zugreifen (es steht eben nur nicht der String drin). Wenn das von mirage228 stimmt, dann wird einfach genügend speicher reserviert für den String und der Index[1] greift prinzipiell eben erst auf die Xte-Stelle des reservierten Speichers zu und nicht wirklich auf die 1te oder 0te

@Muetze: ist klar das es nur bei den AnsiStrings so ist, ansonsten ist ja wie beim PChar alles an einer Stelle und nicht überall im speicher verteilt mit extra speicherverwaltung (die ist eben wegen den referenzzeugs nur bei den Ansistrings dabei)

SebE 16. Jan 2005 14:39

Re: Frage zum Aufbau eines Strings
 
Ich würd' mir gern mal die Length-Funktion anschauen, um näheres zu erfahren, aber ich finde sie nicht in der System.pas :pale:

sakura 16. Jan 2005 14:39

Re: Frage zum Aufbau eines Strings
 
Wurde ja alles schon erwähnt, deswegen nur noch mal kurz zusammenfassend.

Der String ist ein Pointer auf einen Speicherbereich. Selbst also nichts anderes als ein Pointer. Dieser Pointer zeigt auf das erste Zeichen des Strings, welches mit StringVar[1] anzusteuern geht. Vor diesem Zeichen gibt es acht weitere Bytes, deren Struktur wie folgend ist (Copyright Borland, System.pas):
Delphi-Quellcode:
type
  PStrRec = ^StrRec;
  StrRec = packed record
    refCnt: Longint;
    length: Longint;
  end;
Das heisst, die Bytes -8 bis -5 sind der Referenzzähler (wie oft wird dieser String genutzt) und die Bytes -4 bis -1 sind die Länge des Strings.

Zu Pascalzeiten war es anders, die Strings dort waren so aufgebaut, wie es heute die ShortStrings sind. Diese hatten eine maximale Länge von 255 Zeichen. Das 0. Byte (also StringVar[0]) speicherte die Länge des Strings (0-255) und die folgenden Bytes den Inhalt.

Die Strings in den heute gängigen Pascal-Versionen können bis zu 2 GB (nicht 4 GB!) groß werden. Da dieser Wert zum Speichern eindeutig mehr als 1 Byte benötigt, kann dieser Wert (StringVar[0]) für normale Strings auch nicht mehr angesteuert werden (Compiler-Fehlermeldung).

...:cat:...

mirage228 16. Jan 2005 14:40

Re: Frage zum Aufbau eines Strings
 
Zitat:

Zitat von SebE
Ich würd' mir gern mal die Length-Funktion anschauen, um näheres zu erfahren, aber ich finde sie nicht in der System.pas :pale:

Hi,

siehe den Hinweis in der System.pas:

Zitat:

unit System; { Predefined constants, types, procedures, }
{ and functions (such as True, Integer, or }
{ Writeln) do not have actual declarations.}
{ Instead they are built into the compiler }
{ and are treated as if they were declared }
{ at the beginning of the System unit.
}
Die Prozedur ist also wahrscheinlich in den Compiler eingebaut.
Edit: Siehe zusätzlich Post von sakura unter mir ;)

mfG
mirage228

sakura 16. Jan 2005 14:41

Re: Frage zum Aufbau eines Strings
 
Zitat:

Zitat von SebE
Ich würd' mir gern mal die Length-Funktion anschauen, um näheres zu erfahren, aber ich finde sie nicht in der System.pas :pale:

Diese unterliegt auch der "Copmiler-Magic" und hat verschiedene Implementationen mit unterschiedlichen Namen. Die gängigste ist folgende (Copyright © Borland; System.pas):
Delphi-Quellcode:
function _LStrLen(const s: AnsiString): Longint;
{$IFDEF PUREPASCAL}
begin
  Result := 0;
  if Pointer(s) <> nil then
    Result := PStrRec(Integer(s) - sizeof(StrRec)).length;
end;
{$ELSE}
asm
        { ->   EAX str }

        TEST   EAX,EAX
        JE     @@done
        MOV    EAX,[EAX-skew].StrRec.length;
@@done:
end;
{$ENDIF}
...:cat:...

sakura 16. Jan 2005 14:43

Re: Frage zum Aufbau eines Strings
 
Zitat:

Zitat von SirThornberry
0 ist auch außerhalb des zulässigen, trotzdem kann man drauf zugreifen

Falsch, der Compiler gibt Dir bei Strings eine Fehlermeldung ;-)

...:cat:...

SirThornberry 16. Jan 2005 14:43

Re: Frage zum Aufbau eines Strings
 
Oh :oops: weiß gar nicht wie ich auf die 4 GB gekommen bin. Hmm, jetzt wo du mich wieder mal dran erinnert hast das ein String ja nur ein Pointer war (sizeof(String) war ja schon immer 4 (war mir entfallen *schäm*)) ist mir das ganze mit dem speicher auch wieder bissl klarer (und vor allem das mit dem -4, -8).

@Sakura: Der Compiler schon, aber wenn man über den umweg der Pointer drauf zugreift kommt keine AV (so hatt ichs eigentli auch versucht)

jbg 16. Jan 2005 16:16

Re: Frage zum Aufbau eines Strings
 
Zitat:

Zitat von Muetze1
@SirThornberry: Die Einschränkung mit String und DLL gilt nur für AnsiString, da diese mit internen Referenzen arbeiten - im Gegensatz dazu hat man mit WideStrings oder ShortStrings keine Probleme (keine Referenzen).

Diese Erklärung ist falsch. Es liegt nicht am Referenzzähler, sondern daran, dass man bei AnsiStrings "nicht mitbekommt", wenn man Speicher reserviert oder freigibt. Da Delphi einen eigenen Speichermanager mitbringt, der von Windows größere Speicherblöcke anfordert und diese dann in kleineren Blöcken an die Anwendung gibt, braucht der Delphi-Speichermanager eine Liste mit den Blöcken. Hat man nun eine zusätzliche DLL, enthält diese eine eigene Kopie des Delphi-Speichermanagers die ihre eigene Block-Liste besitzt.
Übergibt man nun einen AnsiString an eine DLL und diese verändert diesen, passiert es, dass der AnsiString an den DLL-Speichermanager zum Freigeben übergeben wird, der mit dem Speicherbereich aber gar nichts anfangen kann, weil der AnsiString ja im EXE-Speichermanager reserviert wurde. In diesem Moment kracht es dann.
Bei WideStrings läuft das ganze anders ab. Da WideString hauptsächlich wegen COM eingeführt wurden und auch nicht-Delphi-Anwendungen COM-Server und Clients sein können, war Borland gezwungen, den von Microsoft vorgegebenen WideString-Speichermanager zu benutzen. Deswegen wird bei WideString nicht AllocMem/FreeMem sondern Windows.SysAllocStringLen/SysFreeString aufgerufen.
Bei ShortStrings liegt die Sache ganz anders. ShortStrings werden nicht dynamisch reserviert, sondern werden einfach im Datensegment oder auf dem Stack erzeugt. Beim Stack wird ein "SUB ESP,256" für einen lokale Variable "s: ShortString" durchgeführt. Und dieser Speicher muss auch nicht realloziert werden, weil die 255 Zeichen Maximum sind, weswegen der EXE und DLL-Speichermanager sich nicht in die Quere kommen.
Also alles was mit AllocMem/GetMem/FreeMem arbeitet (dazu gehören AnsiString, dyn. Array, Klassen, New, Dispose, ...) können nur ohne Probleme an eine DLL übergeben werden, wenn diese nur ohne Reallozierung (ReallocMem) oder Freigeben und neu Reservieren zugreift. Bei AnsiStrings ist es aber sehr wahrscheinlich, dass man den String Realloziert ohne das man das direkt im Code geschrieben hat. Wenn man also die Compiler-Magic für AnsiStrings nicht auswendig kennt, sollte man bei AnsiString sehr vorsichtig sein.

Und nun warum das ganze kein Problem bei Packages ist, die ja auch "nur" DLLs sind. Bei Packages hat Borland das DLL-Format dahingehend erweitert, als dass der Compiler alle öffentlichen Funktionen, Methoden und einige spezielle, nur für den Compiler/RTL wichtige Funktionen exportiert. In diesen Compiler-Magic-Funktionen stecken dann so informationen wie welche Unit im Package enthalten sind, ... Diese Informationen erlauben es der RTL nun zu verhindern, dass die BPL einen eigenen Speichermanager bekommt. Somit benutzen die EXE und die BPL ein und denselben Speichermanager, und können somit AllocMem und FreeMem in beiden Modulen ungehindert aufrufen, weil beide auf dieselble Block-List zugreifen.
Die BorlandMM.dll macht nun nichts anderes, als dass sie in der EXE und in der DLL mittels der Unit ShareMem, die per SetMemoryManager den Speichermanager der EXE und der DLL auf die BorlandMM.dll setzt, den gleichen Speichermanager für beide Module anbietet und somit AllocMem und FreeMem in beiden Modulen aufgerufen werden können.

jbg 16. Jan 2005 16:21

Re: Frage zum Aufbau eines Strings
 
Zitat:

Zitat von SirThornberry
@Sakura: Der Compiler schon, aber wenn man über den umweg der Pointer drauf zugreift kommt keine AV (so hatt ichs eigentli auch versucht)

Das liegt daran, dass der Compiler bei S[Index] automatisch eine 1 von Index abzieht. Müsste ungefähr so aussehen
Code:
MOV ESI, S
MOV EDX, [Index]
MOV AL, [ESI+EDX-1]
Wenn mal also immer von 0 ab zählt und dann mit S[Index+1] zugreift, optimiert der Compiler das zu (ungefähr):
Code:
MOV ESI, S
MOV EDX, [Index]
MOV AL, [ESI+EDX]
Also wird die -1 entfernt, was ein ganz kleinen, fast unmessbaren Geschwindigkeitszuwachs bringt. Macht aber die Lesbarkeit ein wenig kaputt.

Wenn man nun die String S in einen Zeiger konvertiert, entfällt das -1 ganz von alleine, weil es sich ja nicht mehr um einen String handelt.


Alle Zeitangaben in WEZ +1. Es ist jetzt 16:08 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