Pourquoi la plupart des exemples Delphi utilisent FillChar () pour initialiser les enregistrements?

je me suis juste demandé, pourquoi la plupart des exemples Delphi utilisent FillChar() pour initialiser des enregistrements.

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.

ici ( http://stanleyxu2005.blogspot.com/2008/01/potential-memory-leak-by-initializing.html ) est ma note sur ce sujet. L'OMI, de déclarer une constante avec la valeur par défaut est une meilleure façon.

26
demandé sur menjaraz 2009-04-24 02:36:18

8 réponses

raisons historiques, la plupart du temps. FillChar() remonte aux jours de Turbo Pascal et a été utilisé à ces fins. Le nom est en fait un peu impropre car alors qu'il est écrit Fill Char (), il est en réalité Fill Byte (). La raison en est que le dernier paramètre peut prendre un octet char ou . Donc FillChar(Foo, SizeOf(Toto), #0) et FillChar(Foo, SizeOf(Toto), 0) sont équivalentes. Une autre source de confusion est que depuis Delphi 2009, FillChar ne remplit encore que des octets, même si Char est l'équivalent de WideChar. En regardant les utilisations les plus communes pour FillChar afin de déterminer si la plupart des gens utilisent FillChar pour remplir la mémoire avec des données de caractères ou juste l'utiliser pour initialiser la mémoire avec une certaine valeur d'octet donné, nous avons trouvé que c'était le dernier cas qui a dominé son utilisation plutôt que le premier. Avec cela nous avons décidé de garder FillChar byte-centric.

il est vrai qu'effacer un enregistrement avec FillChar qui contient un champ déclaré en utilisant l'un des types "managés" (chaînes, variante, Interface, tableaux dynamiques) peut être dangereux s'il n'est pas utilisé dans le contexte approprié. Dans l'exemple que vous avez donné, cependant, il est en fait sûr D'appeler FillChar sur la variable d'enregistrement localement déclarée aussi longtemps que c'est la première chose que vous faites à l'enregistrement dans ce cadre . La raison en est que le compilateur a généré du code pour initialiser le champ string dans l'enregistrement. Cela aura mettez déjà le champ string à 0 (nil). Appeler FillChar(Foo, SizeOf (Foo), 0) va juste écraser l'enregistrement entier avec 0 bytes, y compris le champ string qui est déjà 0. Utiliser FillChar sur la variable d'enregistrement après une valeur a été assignée au champ string, n'est pas recommandé. L'utilisation de votre technique initialisée de constante est une très bonne solution ce problème parce que le compilateur peut générer le code approprié pour s'assurer que les valeurs d'enregistrement existantes sont correctement finalisées lors de l'affectation.

35
répondu Allen Bauer 2009-04-24 03:50:14

si vous avez Delphi 2009 et plus tard, utilisez l'appel Default pour initialiser un enregistrement.

Foo := Default(TFoo); 

Voir de David réponse à la question Comment bien gratuit, des dossiers qui contiennent différents types de Delphes à la fois? .

Edit:

l'avantage d'utiliser l'appel Default(TSomeType) , c'est que le document est finalisé avant d'être effacé. Pas de fuites de mémoire et pas explicite appel de bas niveau dangereux pour FillChar ou ZeroMem. Lorsque les dossiers sont complexes, peut-être contenant des enregistrements imbriqués, etc, le risque de faire des erreurs est éliminé.

votre méthode pour initialiser les enregistrements peut être rendue encore plus simple:

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

parfois vous voulez qu'un paramètre ait une valeur non-par défaut, alors faites comme ceci:

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

Cela permettra d'économiser un peu de temps et l'accent est mis sur les choses importantes.

15
répondu LU RD 2017-05-23 12:34:27

FillChar est très bien pour vous assurer de ne pas obtenir toutes les ordures dans un nouvelles, non initialisée de la structure (enregistrement, le tampon, arrray...).

Il ne doit pas être utilisé pour "réinitialiser" les valeurs sans savoir ce que vous réinitialisez.

Pas plus que d'écrire MyObject := nil et de s'attendre à éviter une fuite de mémoire.

Dans particulart tous les types gérés doivent être surveillés attentivement.

Voir la finaliser fonction.

quand vous avez le pouvoir de jouer directement avec la mémoire, il y a toujours un moyen de vous tirer dans le pied.

9
répondu François 2009-04-24 00:52:35

FillChar est habituellement utilisé pour remplir tableaux ou enregistrements avec seulement des types numériques et tableau. Vous avez raison de dire qu'il ne devrait pas être utilisé lorsqu'il y a chaînes (ou toute variable comptée) dans le record .

bien que votre suggestion d'utiliser un const pour l'initialiser fonctionnerait, un problème entre en jeu quand j'ai un de longueur variable tableau que je veux initialiser.

4
répondu Jim McKeeth 2009-04-23 23:12:37

traditionnellement, un caractère est un octet simple (plus vrai pour Delphi 2009), donc utiliser fillchar avec un #0 initaliserait la mémoire allouée de sorte qu'il ne contenait que des nulls, ou byte 0, ou bin 00000000.

vous devriez plutôt utiliser la fonction ZeroMemory pour la compatibilité, qui a les mêmes paramètres d'appel que l'ancien fillchar.

2
répondu skamradt 2009-04-23 23:19:54

cette question a une implication plus large qui est dans mon esprit depuis des âges. Moi aussi, j'ai été élevée sur L'utilisation de FillChar pour les disques. C'est agréable parce que nous ajoutons souvent de nouveaux champs à l'enregistrement (data) et bien sûr FillChar( Rec, SizeOf( Rec), #0 ) s'occupe de ces nouveaux champs. Si nous "le faisons correctement", nous devons itérer à travers tous les champs du document, dont certains sont des types énumérés, dont certains peuvent être des documents eux-mêmes et le code résultant est moins lisible aussi bien être peut-être erroné si nous n'ajoutons pas de nouveaux champs d'enregistrement à elle avec diligence. Les champs String sont communs, donc FillChar est un non-non maintenant. Il y a quelques mois, j'ai fait le tour et j'ai converti toutes mes FillChars sur des enregistrements avec des champs de chaîne en clearing itéré, mais je n'étais pas satisfait de la solution et je me demande s'il y a une façon soignée de faire le 'Fill' sur des types simples (ordinal / float) et 'Finalize' sur des variantes et des chaînes?

1
répondu Brian Frost 2009-04-24 09:29:35

la question peut aussi se poser:

il n'y a pas de ZeroMemory fonction dans Windows. Dans les fichiers d'en-tête ( winbase.h ) c'est une macro qui, dans le monde C, fait demi - tour et appelle memset:

memset(Destination, 0, Length);

ZeroMemory est le terme de langage neutre pour "la fonction de votre plate-forme qui peut être utilisée pour zéro mémoire"

l'équivalent Delphi de memset est FillChar .

puisque Delphi n'a pas de macros (et avant les jours de lining), appeler ZeroMemory signifie que vous avez dû subir la pénalité d'un appel de fonction supplémentaire avant que vous avez réellement obtenu à FillChar .

ainsi, à bien des égards, appeler FillChar est une micro-optimisation de performance - qui n'existe plus maintenant que ZeroMemory est intitulé:

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

Lecture De Bonus

Windows contient également la fonction SecureZeroMemory . Il fait exactement la même chose que ZeroMemory . Si il fait la même chose que ZeroMemory , Pourquoi existe-t-il?

parce que certains compilateurs C/C++ intelligents pourraient reconnaître que le réglage de la mémoire à 0 avant de se débarrasser de la mémoire est une perte de temps - et optimiser l'appel à ZeroMemory .

Je ne pense pas que le compilateur de Delphi soit aussi intelligent que beaucoup d'autres compilateurs; il n'y a donc pas besoin d'un SecureFillChar .

1
répondu Ian Boyd 2018-08-20 17:43:33

Voici une meilleure façon d'initialiser les choses sans utiliser FillChar:

Enregistrement en cours d'enregistrement (Ne peut pas initialiser)

Comment initialiser un tableau statique?

0
répondu Everybody_hates_BillTheLizard 2017-05-23 10:31:12