Proč většina příkladů Delphi používat FillChar () pro inicializaci záznamů?

hlasů
26

Jen jsem přemýšlel, proč se většina příkladů Delphi používat FillChar () pro inicializaci záznamů.

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.

Zde ( http://stanleyxu2005.blogspot.com/2008/01/potential-memory-leak-by-initializing.html ) je moje poznámka k tomuto tématu. IMO, vyhlásit konstantní Výchozí hodnota je lepší způsob.

Položena 23/04/2009 v 23:36
zdroj uživatelem
V jiných jazycích...                            


8 odpovědí

hlasů
34

Z historických důvodů, většinou. FillChar () sahá až do dob Turbo Pascal a byl použit k těmto účelům. Jméno je opravdu trochu nesprávné pojmenování, protože i když se říká, že Fill Char (), je to opravdu Fill Byte (). Důvodem je to, že poslední parametr může trvat char nebo byte. Takže FillChar (Foo, sizeof (Foo), # 0) a FillChar (Foo, sizeof (Foo), 0) jsou rovnocenné. Další zdroj zmatku je, že Delphi 2009 FillChar stále naplňuje pouze bajtů, přestože Char je ekvivalentní WideChar. Při pohledu na nejčastější použití pro FillChar s cílem zjistit, zda většina lidí používat FillChar skutečně naplnit paměť s znakových dat, nebo jen ji použít k inicializaci paměti s určitou danou byte hodnotu, jsme zjistili, že to byl ten druhý případ, který dominoval jeho použití spíše než první. S tím jsme se rozhodli ponechat FillChar byte-centric.

Je pravda, že vymazání záznamu s FillChar, která obsahuje pole prohlášena jedním z „spravovaných“ typů (řetězce, variantu rozhraní, dynamická pole), může být nebezpečné, pokud nejsou použity ve správném kontextu. V tomto příkladu vám dal, ale to je vlastně bezpečné volat FillChar na místně deklarované záznamu proměnné tak dlouho, jak to je první věc, kterou kdy udělal záznam v rámci tohoto rozsahu . Důvodem je to, že překladač generoval kód pro inicializaci pole řetězce v záznamu. To bude již nastavit pole řetězce na 0 (nula). Volání FillChar (Foo, sizeof (Foo), 0), bude jen přepsat celý záznam s 0 bajtů, včetně pole Řetězec, který je již 0. Použití FillChar na proměnné záznamu poté, co hodnota byla přiřazena pole řetězce, se nedoporučuje , Používání inicializaci konstantní technika je velmi dobrým řešením tohoto problému, protože kompilátor může generovat správný kód, aby se zajistilo, že stávající hodnoty záznam řádně dokončeny během přiřazení.

Odpovězeno 24/04/2009 v 04:50
zdroj uživatelem

hlasů
12

Pokud máte Delphi 2009 a později, použijte Defaultvýzvu k inicializaci záznamu.

Foo := Default(TFoo); 

Viz Davidův odpověď na otázku Jak správně bez záznamů, které obsahují různé typy v Delphi najednou? ,

Upravit:

Výhodou použití Default(TSomeType)hovor, je to, že záznam je dokončen před tím, než je zrušeno. Žádné úniky paměti a žádná explicitní nebezpečné nízká úroveň volání FillChar nebo ZeroMem. Když záznamy jsou složité, možná obsahující vnořené záznamy atd riziko dělat chyby je eliminován.

Vaše metoda pro inicializaci záznamů může být ještě jednodušší:

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

Někdy chcete parametr mít non-výchozí hodnotu, pak to takhle:

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

Tím se ušetří nějaké psaní a zaostřování je nastaven na důležité věci.

Odpovězeno 16/06/2012 v 21:12
zdroj uživatelem

hlasů
9

FillChar je v pořádku, aby se ujistil, nemusíte dostat žádné odpadky v nové neinicializované struktury (záznamu, vyrovnávací paměti, arrray ...).
To by neměl být používán pro „reset“ hodnoty, aniž by věděl, jaké jsou vaše resetování.
Ne víc než jen psaní MyObject := nila čeká, aby se zabránilo úniku paměti.
V particulart je třeba pečlivě sledovat všechny klientské typy.
Viz Finalize funkci.

Když máte moc manipulovat přímo s pamětí, vždy existuje způsob, jak se střílet do nohy.

Odpovězeno 24/04/2009 v 01:52
zdroj uživatelem

hlasů
4

FillChar se obvykle používá k vyplnění Arrays nebo záznamy pouze číselné typy a pole. Máte pravdu, že by neměly být používány, když tam jsou struny (nebo jakékoliv proměnné REF-počítal) v záznamu .

I když váš návrh použití const inicializovat to bude fungovat, problém přichází do hry, když mám s proměnnou délkou pole , které chci inicializovat.

Odpovězeno 24/04/2009 v 00:12
zdroj uživatelem

hlasů
2

Tradičně, charakter je jeden byte (již neplatí pro Delphi 2009), tak s použitím fillchar znakem # 0 by initalize paměti přidělené tak, aby obsahoval pouze nuly, nebo byte 0 nebo bin 00000000.

Místo toho byste měli použít ZeroMemory funkce pro kompatibilitu, který má stejné parametry jako volání starého fillchar.

Odpovězeno 24/04/2009 v 00:12
zdroj uživatelem

hlasů
1

Tato otázka má širší důsledky, které bylo v mé mysli po staletí. I já jsem byl vychován na používání FillChar pro záznamy. To je pěkné, protože jsme často přidávat nové pole do (data) záznamu a samozřejmě FillChar (Rec, sizeof (REC), # 0) pečuje o těchto nových oblastech. Jestli jsme to správně ", máme k iteraci přes všechny poli každé věty, z nichž některé jsou vyjmenované druhy, z nichž některé mohou být záznamy sebe a výsledný kód je méně čitelný i za možná chybné, pokud nemáme přidat nový záznam pole na něj pečlivě. String pole jsou běžné, tedy FillChar je ne-ne teď. Před několika měsíci jsem šel kolem a převede veškeré své FillChars na záznamech s řetězcových polí do iterativní zúčtování, ale nebyl jsem spokojený s roztokem a zajímalo, jestli tam je elegantní způsob, jak dělat ‚Fill‘ na jednoduše typů (pořadové číslo / float) a ‚Finalizace‘ na variantách a smyčce?

Odpovězeno 24/04/2009 v 10:29
zdroj uživatelem

hlasů
0

Otázkou může být také ptát:

Neexistuje žádná ZeroMemory funkce v systému Windows. V hlavičkové soubory ( winbase.h) se jedná o makro, které v C světě, se otočí a zavolá memset:

memset(Destination, 0, Length);

ZeroMemory je jazyk neutrální výraz pro „funkci vaší platformy, které mohou být použity k nulové paměti“

Delphi ekvivalent memsetje FillChar.

Vzhledem k tomu, Delphi nemá makra (a předtím, než dny inlining), volat ZeroMemory znamenalo, že jste musel trpět trest extra volání funkce, než je skutečně dostal do FillChar .

Tak v mnoha ohledech, volat FillChar je výkon mikro-optimalizace - který již neexistuje teď ZeroMemory je inline:

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

Bonus Reading

Windows také obsahuje SecureZeroMemory funkci. To dělá přesně to samé jako ZeroMemory . Pokud se tak stane totéž, co ZeroMemory , proč vůbec existuje?

Vzhledem k tomu z nějakého inteligentního C / C ++ kompilátory mohl poznat, že nastavení paměti 0před zbavit paměti je ztráta času - a optimalizovat pryč volání ZeroMemory .

Nemyslím si, že Delphi kompilátor je tak chytrý jako mnoho jiných kompilátorů; takže není potřeba SecureFillChar .

Odpovězeno 20/08/2018 v 17:43
zdroj uživatelem

hlasů
0

Zde je lepší způsob, jak inicializovat věci bez použití FillChar:

Záznam v záznamu (Nelze inicializovat)
Jak inicializovat statické pole?

Odpovězeno 12/07/2011 v 19:33
zdroj uživatelem

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more