Pourquoi la plupart Delphi exemples d'utilisation FillChar() pour initialiser les registres?
J'ai juste demandé pourquoi la plupart des Delphi exemples d'utilisation FillChar() pour initialiser les registres.
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.
Vous devez vous connecter pour publier un commentaire.
Des raisons historiques, la plupart du temps. FillChar() remonte à la Turbo Pascal jours et a été utilisé à de telles fins. Le nom est vraiment un peu un abus de langage car il dit RemplissezChar(), c'est vraiment RemplirOctet(). La raison en est que le dernier paramètre peut prendre un caractère ou un octet. Donc FillChar(Foo, SizeOf(Toto), #0) et FillChar(Foo, SizeOf(Toto), 0) sont équivalentes. Une autre source de confusion est que de Delphi 2009, FillChar encore seulement remplit octets, même si Char est équivalent à WideChar. En regardant les utilisations les plus courantes pour FillChar afin de déterminer si la plupart des gens utilisent FillChar remplir la mémoire avec des données de caractère, ou simplement l'utiliser pour initialiser la mémoire avec une certaine valeur d'un octet, nous avons trouvé que c'était le dernier cas qui a dominé son utilisation plutôt que de l'ancien. Avec qui nous avons décidé de garder FillChar octet-centrique.
Il est vrai que la compensation d'un enregistrement avec FillChar qui contient un champ déclaré à l'aide de l'un des "géré" types (chaînes de caractères, Variante, l'Interface, les tableaux dynamiques) peuvent être dangereux si pas utilisé dans le contexte approprié. Dans l'exemple que vous avez donné, cependant, il est réellement sans danger pour appeler FillChar sur l'déclarés localement variable d'enregistrement aussi longtemps que c'est la première chose que vous avez à faire pour l'enregistrer dans cette étendue. La raison en est que le compilateur a généré le code d'initialisation de la chaîne de champ dans l'enregistrement. Ce sera déjà le champ de type chaîne de 0 (nul). L'appel de FillChar(Foo, SizeOf(Toto), 0) permet de remplacer simplement l'ensemble de l'enregistrement avec 0 octets, y compris la chaîne de terrain qui est déjà à 0. À l'aide de FillChar sur la variable d'enregistrement après une valeur a été attribuée à la chaîne de champ, n'est pas recommandée. À l'aide de votre initialisé constante de la technique est une très bonne solution à ce problème, car le compilateur peut générer le bon code pour s'assurer de l'enregistrement existant valeurs sont correctement finalisé au cours de la mission.
Si vous avez Delphi 2009 et versions ultérieures, utilisez le
Default
appel à initialiser un record.Voir La réponse de David à la question Comment bien gratuit, des dossiers qui contiennent différents types de Delphes à la fois?.
Edit:
L'avantage de l'utilisation de la
Default(TSomeType)
appel, c'est que le dossier est finalisé avant l'autorisation. Pas de fuites de mémoire et explicite pas dangereux faible niveau de l'appel à 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 dossiers peuvent être encore plus simple:
Parfois, vous voulez un paramètre d'avoir une non-valeur par défaut, puis faire comme ceci:
Cela permettra d'économiser un peu de temps et l'accent est mis sur les choses importantes.
FillChar est très bien pour vous assurer de ne pas obtenir toutes les ordures dans un nouveau, non initialisée structure (enregistrement, le tampon, arrray...).
Il ne devrait pas être utilisé pour "reset", les valeurs sans savoir ce que vous êtes en train de réinitialiser.
Pas plus que d'écrire
MyObject := nil
et s'attendant à éviter les fuites de mémoire.Dans particulart tous les types gérés doivent être surveillés attentivement.
Voir la Finaliser fonction.
Lorsque vous avez le pouvoir de jouer directement avec la mémoire, il y a toujours un moyen de se tirer dans le pied.
FillChar est généralement utilisé pour remplir Tableaux ou enregistrements avec seulement les types numériques et tableau. Vous avez raison qu'il ne devrait pas être utilisé quand il y a chaînes (ou toute réf compte de variables) dans le enregistrement.
Bien que votre suggestion de l'utilisation d'un const pour initialiser ça marcherait, un problème entre dans le jeu quand j'ai un de longueur variable tableau que je veux initialiser.
La question peut également 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 C monde, se retourne et appelle memset:ZeroMemory est le langage le terme neutre pour "votre plate-forme de la fonction qui peut être utilisée à zéro de la mémoire"
Delphi équivalent de
memset
estFillChar
.Depuis Delphi n'a pas de macros (et avant le jour de l'inlining), l'appel de ZeroMemory signifiait souffrir la peine d'ajouter un appel de fonction avant d'arriver à FillChar.
Donc, à bien des égards, l'appel de FillChar est une performance en micro-optimisation - qui n'existe plus maintenant que ZeroMemory est incorporé:
Bonus De Lecture
Windows contient également de la SecureZeroMemory fonction. Il fait exactement la même chose que ZeroMemory. Si il fait la même chose que ZeroMemory, pourquoi existe-t-elle?
Parce que certains smart compilateurs C/C++ peut reconnaître que l'établissement de la mémoire à
0
avant de vous débarrasser de la mémoire est un gaspillage de temps et d'optimiser loin l'appel à ZeroMemory.Je ne pense pas que Delphi compilateur est aussi intelligent que de nombreux autres compilateurs; donc il n'y a pas besoin d'un SecureFillChar.
Traditionnellement, un personnage, un seul octet (qui n'est plus vrai pour Delphi 2009), l'utilisation d'un fillchar avec un #0 initalize la mémoire allouée pour qu'il ne contient que des zéros, ou byte 0, ou bin 00000000.
Vous devriez plutôt utiliser la ZeroMemory fonction pour la compatibilité, qui a les mêmes paramètres d'appel que l'ancien fillchar.
Cette question a des implications plus larges, qui a été dans mon esprit depuis des siècles. Moi aussi, j', a été porté sur l'utilisation de FillChar pour les enregistrements. C'est sympa car nous avons souvent ajouter de nouveaux champs à la (des données) enregistrement et de cours FillChar( Rec, SizeOf( Rec), #0 ) prend soin de ces nouveaux champs. Si nous le faisons correctement", nous avons pour itérer sur tous les champs de l'enregistrement, certains sont des types énumérés, dont certains peuvent être des enregistrements eux-mêmes et que le code résultant est moins lisible ainsi être éventuellement erronées si nous n'avons pas d'ajouter de nouveaux champs d'enregistrement de manière diligente. Les champs de type chaîne sont communs, ainsi FillChar n'est pas maintenant. Il y A quelques mois, j'ai converti tous mes FillChars sur des enregistrements avec des champs de type chaîne de itéré de compensation, mais je n'étais pas heureux avec la solution et je me demande si il existe un moyen sympa de faire du "Remplissage" il suffit de types (ordinal /float) et "Finaliser" sur les variantes et les chaînes de caractères?
Ici est une meilleure façon d'initialiser des trucs sans l'aide de FillChar:
De Record en record (Ne peut pas initialiser)
Comment initialiser un tableau statique?