Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Move und FillChar bei strings. (https://www.delphipraxis.net/180684-move-und-fillchar-bei-strings.html)

Bjoerk 8. Jun 2014 08:56

Delphi-Version: 2007

Move und FillChar bei strings.
 
Ich hab manche Routinen die Geschwindigkeitsoptimiert sind und verwende da deshalb (gelegentlich) eigene Stringlisten (FItems: array of string). Jetzt wollte ich mal fragen ob die Moves und FillChars so richtig sind und ob der Compiler was mit SizeOf(string) anfangen kann? :gruebel:
Delphi-Quellcode:
procedure TMyStringList.Insert(const Index: integer; const Value: string);
var
  N: integer;
begin
  SetCount(FCount + 1);
  if Index < FCount - 1 then
  begin
    N:= (FCount - 1 - Index) * SizeOf(string);
    Move(FItems[Index], FItems[Index + 1], N);
    FillChar(FItems[Index], SizeOf(string), 0);
  end;
  FItems[Index] := Value;
end;

procedure TMyStringList.Delete(const Index: integer);
var
  N: integer;
begin
  if Index < FCount - 1 then
  begin
    N:= (FCount - 1 - Index) * SizeOf(string);
    Move(FItems[Index + 1], FItems[Index], N);
    FillChar(FItems[FCount - 1], SizeOf(string), 0);
  end;
  SetCount(FCount - 1);
end;

DeddyH 8. Jun 2014 09:29

AW: Move und FillChar bei strings.
 
Lass Dir doch einfach mal den Wert von SizeOf(string) ausgeben, da dürfte 4 bzw. 8 bei herauskommen, da es sich intern ja um einen Pointer handelt.

Bjoerk 8. Jun 2014 09:47

AW: Move und FillChar bei strings.
 
Ja, bei mir (Ansi) kommt immer 4 raus, ejal wie lang der string ist?
BTW, bei Delete bekomme ich ein Speicherloch!?

DeddyH 8. Jun 2014 09:52

AW: Move und FillChar bei strings.
 
Ja eben, es ist unter 32 Bit immer 4 (Pointergröße). Um die Größe in Bytes zu ermitteln, müsstest Du die Länge mit SizeOf(char) multipilizieren.

Bjoerk 8. Jun 2014 10:47

AW: Move und FillChar bei strings.
 
Du meinst bei Fillchar? OK, hab ich geändert. Speicherloch bleibt aber?

Delphi-Quellcode:
  FillChar(FItems[..], Length(FItems[..]) * SizeOf(char), 0);

nuclearping 8. Jun 2014 14:38

AW: Move und FillChar bei strings.
 
Delphi-Quellcode:
Finalize(FItems[...]);
bzw.
Delphi-Quellcode:
Finalize(FItems);
?

Bjoerk 8. Jun 2014 15:42

AW: Move und FillChar bei strings.
 
Nee. Leider. Hatte auch schon SetLength(FItems[], 0) probiert. Macht es nicht. Das Move scheint wohl so auch nicht zu gehen selbst wenn ich die Puffergröße in Bytes genau ausrechne?

TiGü 8. Jun 2014 17:45

AW: Move und FillChar bei strings.
 
Siehe hier:
http://www.delphipraxis.net/1255938-post2.html

Also anstatt
Delphi-Quellcode:
FillChar(FItems[Index], SizeOf(string), 0);
lieber mal
Delphi-Quellcode:
FItems[Index] := '';
.

Bjoerk 8. Jun 2014 18:04

AW: Move und FillChar bei strings.
 
Ok. aber was ist dann mit dem Referenzzähler?

Ich bin mit wegen den strings nicht sicher und bezweifele langsam ob das so geht und ob’s überhaupt an dem Finalize liegt? Mir scheint man kann auch bei strings nicht so moven? Bei Shortstring funktionierst hingegen einwandfrei?

Ich poste mal die Klasse:

Delphi-Quellcode:
destructor TMyStringList.Destroy;
begin
  Clear;
  inherited Destroy;
end;

procedure TMyStringList.Clear;
begin
  FCount := 0;
  Capacity := 0;
end;

function TMyStringList.DeltaCapacity: integer;
begin
  if FCapacity > 64 then
    Result := FCapacity div 4
  else
    if FCapacity > 8 then
      Result := 16
    else
      Result := 4;
end;

function TMyStringList.Add(const Value: string): integer;
begin
  Result := FCount;
  Insert(Result, Value);
end;

procedure TMyStringList.Insert(const Index: integer; const Value: string);
begin
  if FCount = FCapacity then
    Capacity := FCapacity + DeltaCapacity; // property Capacity: integer write SetCapacity;
  if Index < FCount then
  begin
    Move(FItems[Index], FItems[Index + 1], (FCount - Index) * SizeOf(string));
    Finalize(FItems[Index]);
  end;
  FItems[Index] := Value;
  Inc(FCount);
end;

procedure TMyStringList.Delete(const Index: integer);
begin
  Dec(FCount);
  if Index < FCount then
  begin
    Move(FItems[Index + 1], FItems[Index], (FCount - Index) * SizeOf(string));
    Finalize(FItems[FCount]);
  end;
  FItems[FCount] := '';
end;

function TMyStringList.GetItems(Index: integer): string;
begin
  Result := FItems[Index];
end;

procedure TMyStringList.SetItems(Index: integer; const Value: string);
begin
  FItems[Index] := Value;
end;

procedure TMyStringList.SetCapacity(const Value: integer);
begin
  FCapacity := Value;
  SetLength(FItems, FCapacity);
end;

himitsu 8. Jun 2014 18:55

AW: Move und FillChar bei strings.
 
PS: Wenn man probemlos PChars kopieren will, warum verwendet man nicht darür die entsprechenden APIs, welche für PChars vorgesehen sind? :roll:
Diese zählen die Länge in Chars, was dann überall stimmt., egal ob Unicode oder nicht, ohne daß man auf die Größe der Chars achten muß.

StrMove, StrLMove, StrECopy, StrLCopy, StrPCopy, ... (siehe SysUtils)

Bjoerk 8. Jun 2014 20:18

AW: Move und FillChar bei strings.
 
Delphi-Quellcode:
function StrMove(Dest: PChar; const Source: PChar; Count: Cardinal): PChar;
begin
  Result := Dest;
  Move(Source^, Dest^, Count);
end;
Da kann man doch auch gleich Move(Source, Dest, Count) schreiben, oder?

My Problem is, ich wäß ned was (hier) als Count anzusetzen is? :oops:

himitsu 8. Jun 2014 20:23

AW: Move und FillChar bei strings.
 
Natürlich so, wie man es von C++ und Co. auch kennt.

Also das CharCount.


Drum sieht das seit Delphi 2009 in etwa so aus, seitdem das PChar kein PAnsiChar mehr ist. :zwinker:
Delphi-Quellcode:
{$IFNDEF NEXTGEN}
function StrMove(Dest: PAnsiChar; const Source: PAnsiChar; Count: Cardinal): PAnsiChar;
begin
  Result := Dest;
  Move(Source^, Dest^, Count * SizeOf(AnsiChar));
end;
{$ENDIF !NEXTGEN}

function StrMove(Dest: PWideChar; const Source: PWideChar; Count: Cardinal): PWideChar;
begin
  Result := Dest;
  Move(Source^, Dest^, Count * SizeOf(WideChar));
end;

Bjoerk 8. Jun 2014 20:45

AW: Move und FillChar bei strings.
 
Ok. Thanx. Dann kann man das aber so nicht machen (Integerüberlauf). Ich lass es jetzt sein. :wall:

Delphi-Quellcode:
function TMyStringList.MoveBytes(const Index: integer): integer;
var
  I: integer;
begin
  Result := 0;
  for I := Index to FCount - 1 do
    Inc(Result, Length(FItems[I]) * SizeOf(Char));
end;

himitsu 8. Jun 2014 21:32

AW: Move und FillChar bei strings.
 
Also vom Code her sieht es OK aus und sollte so auch funktionieren.

Aber wo werden da Bytes gemovet und wenn ja Welche?

Bjoerk 8. Jun 2014 22:27

AW: Move und FillChar bei strings.
 
Zuletzt hatte ich es so. Geht aber nicht. Bringt sogar gelegentlich EInavlidPointer AV.
Delphi-Quellcode:
function TMyStringList.Add(const Value: string): integer;
begin
  Result := FCount;
  Insert(Result, Value);
end;

function TMyStringList.MoveBytes(const Index: integer): integer;
var
  I: integer;
begin
  Result := 0;
  for I := Index to FCount - 1 do
    Result := Result + Length(FItems[I]) * SizeOf(Char);
end;

procedure TMyStringList.Insert(const Index: integer; const Value: string);
begin
  if FCount = FCapacity then
    Capacity := FCapacity + DeltaCapacity;
  if Index < FCount then
  begin
    Move(FItems[Index], FItems[Index + 1], MoveBytes(Index));
    // FillChar(FItems[Index], Length(FItems[Index]) * SizeOf(Char), 0);
    Finalize(FItems[Index]);
  end;
  FItems[Index] := Value;
  Inc(FCount);
end;

procedure TMyStringList.Delete(const Index: integer);
begin
  if Index < FCount - 1 then
  begin
    Move(FItems[Index + 1], FItems[Index], MoveBytes(Index));
    // FillChar(FItems[FCount - 1], Length(FItems[FCount - 1]) * SizeOf(Char), 0);
    Finalize(FItems[FCount - 1]);
  end;
  FItems[FCount - 1] := '';
  Dec(FCount);
end;

Insider2004 9. Jun 2014 07:04

AW: Move und FillChar bei strings.
 
Für was soll das alles gut sein? Nimm TStringList und fertig!

himitsu 9. Jun 2014 10:02

AW: Move und FillChar bei strings.
 
Delete:
Erst Freigeben (Finalize), und zwar das, was du löschst. :warn:
dann verschieben, was vor muß und das, was verschoben, aber nicht gelöscht wurde, leeren FillChar, sonst ist der Eintrag doppelt da und du hast somit die Referenzzählung zerschossen.
Oder das erst den zu Löschenden zuwischenspeichern (Move in ine Tempvariable), dann verschieben und den zu löschenden an letzter Stelle einfügen (Movo) und die TempVariable natürlich, wenn man dafür den OriginalTyp verwendete, ebenfalls Nullen, da dieses sonst auch doppelt ist.

Genauso beim Insert.


Fazit: Erst lernen wie eigentlich die Speicherverwaltung funktioniert und dann sich an soein Projekt rantrauen.

Oder du fummelst besser nicht an der Speicherverwaltung rum
Delphi-Quellcode:
procedure ArrayDelete(var Arr: TMyArray; Index: Integer);
var
  i: Integer;
begin
  for i := Index to High(Arr) - 1 do
    Arr[i] := Arr[i + 1];
  SetLength(Arr, Length(Arr) - 1);
end;
Oder du nutzt fertige Codes, welche sich darum kümmern, wie z.B. die generische TList<>.

Bjoerk 9. Jun 2014 11:12

AW: Move und FillChar bei strings.
 
Dann muß man den Index aber aussparen (***).

Und wie gesagt, ich laß den Blödsinn jetzt. :cyclops:

Delphi-Quellcode:
procedure TMyStringList.Delete(const Index: integer);
begin
  Dec(FCount); // ***
  if Index <= FCount then
  begin
    Finalize(FItems[Index]);
    if Index < FCount then
    begin
      Move(FItems[Index + 1], FItems[Index], (FCount - Index) * SizeOf(string));
      FillChar(FItems[FCount], Length(FItems[FCount]) * SizeOf(Char), 0);
    end;
  end;
  FItems[FCount] := '';
end;

himitsu 9. Jun 2014 11:35

AW: Move und FillChar bei strings.
 
Was ist denn FItems nun eigentlich?

Die Char-Größe muß man nur einrechnen, wenn sich die Große auf Chars bezieht.
Wenn sich das nur auf einen String-Pointer und nicht auf den String-Inhalt bezieht, dann hat das dort absolut nichts zu suchen.

Bjoerk 9. Jun 2014 11:59

AW: Move und FillChar bei strings.
 
FItems: array of string; (Siehe auch #1)

Bjoerk 9. Jun 2014 18:19

AW: Move und FillChar bei strings.
 
Ich hab mir jetzt TStrings/ TStringList doch mal näher angeschaut (ist strukturell sehr ähnlich TList (die kenne ich fast auswendig)). Schon erstaunlich daß man von einer Klasse ableitet die überhaupt keine Items hat? BTW, was ich auch nie verstehe warum man da nicht MaxListSize TStringItems erzeugt? (oder doch)?

Delphi-Quellcode:
  TStringList = class;

  PStringItem = ^TStringItem;
  TStringItem = record
    FString: string;
    FObject: TObject;
  end;

  PStringItemList = ^TStringItemList;
  TStringItemList = array[0..MaxListSize] of TStringItem;
  TStringListSortCompare = function(List: TStringList; Index1, Index2: Integer): Integer;

  TStringList = class(TStrings)
  private
    FList: PStringItemList;

Blup 10. Jun 2014 09:26

AW: Move und FillChar bei strings.
 
Bei Angabe eines ungültigen Index, darf sich die Liste überhaupt nicht verändern.
Das Fillchar muss die Stringvariable löschen, nicht den Inhalt des Strings.
Delphi-Quellcode:
procedure TMyStringList.Delete(const Index: integer);
begin
  if (Index < 0) or (Index >= FCount) then
    raise Exception.Create(...);

  Dec(FCount);
  Finalize(FItems[Index]);
  if Index < FCount then
    Move(FItems[Index + 1], FItems[Index], (FCount - Index) * SizeOf(string));

  FillChar(FItems[FCount], SizeOf(string), 0);
end;
Auch der Aufruf Finalize und die abschießende Zuweisung eines leeren Strings kann problematisch sein, wenn der Stringpointer vorher nicht auf nil gesetzt wird (FillChar).
Finalize verringert eigentlich nur den Referenzzähler des Strings auf den die Variable bisher verweist (string wird freigegeben bei RefCount = 0).
Wird aber danach der Variablen wieder ein Wert zugewiesen und die Variable zeigt noch auf den alten String, wird das erneut versucht.


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