Warum die meisten Delphi Beispiele FillChar () verwenden, um Datensätze zu initialisieren?

StackOverflow https://stackoverflow.com/questions/783825

  •  13-09-2019
  •  | 
  •  

Frage

ich nur gefragt, warum die meisten Delphi Beispiele FillChar () verwenden, Datensätze zu initialisieren.

type
  TFoo = record
    i: Integer;
    s: string; // not safe in record, better use PChar instead
  end;

const
  EmptyFoo: TFoo = (i: 0; s: '');

procedure Test;
var
  Foo: TFoo;
  s2: string;
begin
  Foo := EmptyFoo; // initialize a record

  // Danger code starts
  FillChar(Foo, SizeOf(Foo), #0);
  s2 := Copy("Leak Test", 1, MaxInt); // The refcount of the string buffer = 1
  Foo.s = s2; // The refcount of s2 = 2
  FillChar(Foo, SizeOf(Foo), #0); // The refcount is expected to be 1, but it is still 2
end;
// After exiting the procedure, the string buffer still has 1 reference. This string buffer is regarded as a memory leak.

Hier ( http: //stanleyxu2005.blogspot. com / 2008/01 / Potential-Speicher-Leck-by-initializing.html ) ist meine Anmerkung zu diesem Thema. IMO, eine konstante erklären mit Standardwert ein besserer Weg ist.

War es hilfreich?

Lösung

Historische Gründe, meistens. FillChar () geht zurück auf die Pascal Tage Turbo und wurde für solche Zwecke verwendet. Der Name ist wirklich ein wenig irreführend, denn während es sagt, geben Sie bitte Char (), ist es wirklich geben Sie bitte Byte (). Der Grund dafür ist, dass der letzte Parameter ein Zeichen nehmen oder ein Byte. So FillChar (Foo, SizeOf (Foo), # 0) und FillChar (Foo, SizeOf (Foo), 0) entsprechen. Eine weitere Quelle der Verwirrung ist, dass ab Delphi 2009, FillChar noch füllt nur Bytes, obwohl Char entspricht WideChar. Während seiner Zeit bei den häufigsten Anwendungen für FillChar suchen, um zu bestimmen, ob die meisten Leute FillChar verwenden, um tatsächlich Speicher mit Zeichendaten zu füllen oder es nur Speicher mit einem gegebenen Byte-Wert zu initialisieren, verwenden, fanden wir, dass es der letzte Fall war, die seine Verwendung dominiert eher als die ersteren. Damit haben wir uns entschlossen FillChar Byte-centric.

zu halten

Es ist wahr, dass eine Aufzeichnung mit FillChar Clearing, die ein Feld enthält, erklärte einer der „verwalteten“ Typen (Strings, Variant, Schnittstellen, dynamische Arrays) kann gefährlich sein, wenn nicht in den richtigen Kontext verwendet. Im Beispiel Sie gab jedoch ist es eigentlich sicher FillChar auf dem lokal deklarierten Datensatz Variable aufrufen , solange es das erste, was Sie jemals in diesem Anwendungsbereich auf den Datensatz tun. Der Grund dafür ist, dass der Compiler Code erzeugt hat das String-Feld in der Aufzeichnung zu initialisieren. Dies wird bereits das String-Feld auf 0 (Null) gesetzt. Der Aufruf FillChar (Foo, SizeOf (Foo), 0) wird nur den gesamten Datensatz mit 0 Bytes überschrieben, einschließlich der String-Feld, das bereits 0. Mit FillChar auf dem Aufzeichnungs Variable nach ein Wert zugewiesen wurde das String-Feld, wird nicht empfohlen. eine sehr gute Lösung für dieses Problem ist Ihre initialisiert Konstante Technik, da der Compiler den richtigen Code generieren kann die bestehenden Rekordwerte korrekt bei der Zuordnung abgeschlossen zu gewährleisten.

Andere Tipps

Wenn Sie Delphi 2009 und höher den Default Anruf einen Datensatz zu initialisieren.

Foo := Default(TFoo); 

Siehe David Antwort auf die Frage: Wie man richtig frei Datensätze, die auf einmal verschiedene Typen in Delphi enthalten? .

Edit:

Der Vorteil des Default(TSomeType) Aufruf zu verwenden, ist, dass der Datensatz abgeschlossen wird, bevor es gelöscht wird. Keine Speicherlecks und kein expliziter gefährlich niedriges Niveau Aufruf FillChar oder ZeroMem. Wenn die Aufzeichnungen komplex sind, vielleicht verschachtelte Aufzeichnungen usw. enthält, das Risiko, Fehler zu machen beseitigt wird.

Ihre Methode der Datensätze initialisieren kann noch einfacher gemacht werden:

const EmptyFoo : TFoo = ();
...
Foo := EmptyFoo; // Initialize Foo

Manchmal wollen Sie ein Parameter, der einen Nicht-Standardwert haben, dann gehen sie wie folgt:

const PresetFoo : TFoo = (s : 'Non-Default'); // Only s has a non-default value

Dies wird einige Tipparbeit ersparen und der Fokus auf die wichtigen Dinge gesetzt.

FillChar ist in Ordnung, um sicherzustellen, dass Sie nicht bekommen, keinen Müll in einer neuen, nicht initialisierte Struktur (record, Puffer, arrray ...).
Es sollte nicht auf „Reset“ die Werte verwendet werden, ohne zu wissen, was Ihr werden zurückgesetzt wird.
Nicht mehr als nur MyObject := nil schreiben und rechnen zu einem Speicherverlust zu vermeiden.
In particulart sind alle verwalteten Typen sorgfältig beobachtet werden.
Sehen Sie die Finalisierung Funktion.

Wenn Sie die Macht haben, direkt zur Geige mit dem Speicher, gibt es immer eine Möglichkeit, sich in den Fuß zu schießen.

FillChar in der Regel verwendet, um zu füllen Arrays oder Aufzeichnungen mit nur numerischen Typen und Arrays. Sie sind richtig, dass es nicht verwendet werden sollte, wenn es Strings (oder irgendwelche ref gezählt Variablen) in der Datensatz .

Auch wenn Sie Ihren Vorschlag zur Verwendung eines const um es zu initialisieren funktionieren würde, ein Problem, kommt ins Spiel, wenn ich ein mit variabler Länge array , die ich möchte initialisieren.

Die Frage auch fragen können:

Es gibt keine Zeromemory Funktion in Windows. In den Header-Dateien (winbase.h) es ist ein Makro, das in der C-Welt, dreht sich um und ruft Memset:

memset(Destination, 0, Length);

Zeromemory ist die Sprache, neutrale Begriff für „-Funktion für ihre Plattform, die auf Null-Speicher verwendet werden kann“

Die Delphi-Äquivalent memset ist FillChar.

Da Delphi haben keine Makros (und vor den Tagen der inlining), ruft Zeromemory bedeutet Sie die Strafe eines zusätzlichen Funktionsaufruf zu leiden hatte, bevor Sie tatsächlich zu bekommen FillChar .

So in vielerlei Hinsicht fordern FillChar ist eine High-Performance-Optimierung - die nicht mehr existiert jetzt, dass Zeromemory ist inlined:

procedure ZeroMemory(Destination: Pointer; Length: NativeUInt); inline;

Bonus Lesen

Fenster enthält auch die SecureZeroMemory Funktion. Es tut genau dasselbe wie Zeromemory . Wenn es tut dasselbe wie Zeromemory , warum existiert es?

Da einige intelligente C / C ++ Compiler kann erkennen, dass Speichereinstellung vor dem 0 des Speichers loszuwerden eine Verschwendung von Zeit -. Und das Gespräch zu optimieren weg Zeromemory

Ich glaube nicht, Delphi-Compiler so klug, wie viele andere Compiler ist; so gibt es keine Notwendigkeit für ein SecureFillChar .

Traditionell ist ein Zeichen, ein einzelne Byte (nicht mehr gilt für Delphi 2009), so mit einem # 0 mit FillChar würde den Speicher zugewiesen initialisieren, so dass es nur NULL-Werte enthalten ist, oder Byte 0, oder ist 00000000

Sie sollten stattdessen die Zeromemory Funktion für die Kompatibilität, die die gleichen Aufrufparameter wie die alte FillChar hat.

Diese Frage hat eine breitere Bedeutung, die seit Jahrhunderten in meinem Kopf war. Ich war auch auf der Verwendung FillChar für Aufzeichnungen gebracht. Das ist schön, weil wir oft neue Felder in die (Daten) Datensatz hinzufügen und natürlich FillChar (Rec, SizeOf (Rec), # 0) kümmert sich um solche neuen Felder. Wenn wir tun, es richtig ', müssen wir über alle Felder des Datensatzes durchlaufen, von denen einige Arten aufgezählt, selbst von denen einige können Aufzeichnungen und der resultierende Code ist weniger lesbar als auch möglicherweise fehlerhaft sein, wenn wir hinzufügen, nicht neu Datensatzfelder, um es fleißig. String-Felder sind häufig, so FillChar ist ein no-no jetzt. Vor ein paar Monaten, ging ich um und konvertiert alle meine FillChars auf Datensätze mit String-Felder auf iterierende Lichtung, aber ich war mit der Lösung nicht zufrieden und fragen, ob es eine saubere Art und Weise ist die ‚Fill‘ zu tun, auf einfach Arten (Ordnungs / float) und 'Finalisierung' auf Varianten und Strings?

Hier ist ein besserer Weg, Sachen zu initialisieren, ohne FillChar mit:

Rekord in Datensatz (kann nicht initialisiert werden)
Wie ein statisches Array initialisieren?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top