Je parlais à un collègue l'autre jour sur la façon dont vous pouvez fuir une chaîne dans Delphi si vous vraiment gâcher les choses. Par défaut, les chaînes sont comptées et attribuées automatiquement, de sorte qu'elles fonctionnent généralement sans aucune réflexion - pas besoin d'allocation manuelle, de calculs de taille ou de gestion de la mémoire. Mais je me souviens avoir lu une fois qu'il y avait un moyen de fuir une chaîne directement (sans l'inclure dans un objet qui fuit). Il semble que cela ait quelque chose à voir avec le passage d'une chaîne par référence et l'accès à partir d'une plus grande portée à partir de la routine à laquelle elle a été transmise. Oui, je sais que c'est vague, c'est pourquoi je pose la question ici.Comment faire pour fuir une chaîne dans Delphi
Répondre
En fait, la chaîne passe comme CONST ou non const sont les mêmes en terme de nombre de référence Delphi 2007 et 2009. Il y avait un cas qui causait une violation d'accès lorsque la chaîne est passée comme CONST. Voici le problème un
type
TFoo = class
S: string;
procedure Foo(const S1: string);
end;
procedure TFoo.Foo(const S1: string);
begin
S:= S1; //access violation
end;
var
F: TFoo;
begin
F:= TFoo.create;
try
F.S := 'S';
F.Foo(F.S);
finally
F.Free;
end;
end.
Je ne connais pas le problème dans votre deuxième paragraphe, mais j'ai été mordu une fois par des fuites dans un enregistrement.
Si vous appelez FillChar() sur un enregistrement contenant des chaînes, vous écrasez le compte ref et l'adresse de la mémoire allouée dynamiquement avec des zéros. À moins que la chaîne ne soit vide, cela entraînera une fuite de la mémoire. Le moyen de contourner cela est d'appeler Finaliser() sur l'enregistrement avant d'effacer la mémoire qu'il occupe.
Malheureusement, l'appel de Finalize() lorsqu'il n'y a aucun membre de l'enregistrement nécessitant une finalisation entraîne un indice du compilateur. Il m'est arrivé que j'ai commenté l'appel Finalize() pour faire taire l'indice, mais plus tard, quand j'ai ajouté un membre de la chaîne à l'enregistrement, je n'ai pas manqué de commenter l'appel, donc une fuite a été introduite. Heureusement, j'utilise généralement le gestionnaire de mémoire FastMM dans les paramètres les plus verbeux et paranoïaques en mode débogage, donc la fuite n'est pas passée inaperçue. L'indice du compilateur n'est probablement pas une bonne chose, en omettant silencieusement l'appel Finalize() si ce n'est pas nécessaire serait beaucoup mieux à mon humble avis.
> omettre silencieusement l'appel Finalize() si ce n'est pas nécessaire serait beaucoup mieux IMHO +1 –
savez-vous si les chaînes courtes affectées par cela? –
@JamesB: Non, les chaînes courtes ne sont pas affectées par ceci, car elles ne sont pas comptées par référence et ne sont pas constituées de pointeurs vers des segments de mémoire qui pourraient être perdus. Les chaînes courtes sont constituées d'un octet de longueur unique et des données de caractères, donc les remplir avec '0's définit simplement la longueur à' 0' et définit tous les éléments à '# 0'. – mghie
Non, je ne pense pas qu'une telle chose puisse arriver. Il est possible qu'une variable de chaîne obtienne une valeur inattendue, mais qu'elle ne fuit pas la mémoire. Considérez ceci:
var
Global: string;
procedure One(const Arg: string);
begin
Global := '';
// Oops. This is an invalid reference now. Arg points to
// what Global used to refer to, which isn't there anymore.
writeln(Arg);
end;
procedure Two;
begin
Global := 'foo';
UniqueString(Global);
One(Global);
Assert(Global = 'foo', 'Uh-oh. The argument isn''t really const?');
end;
Voici l » argument One
est déclaré const, donc soi-disant, il ne changera pas. Mais alors One
contourne cela en changeant le paramètre réel au lieu du paramètre formel. La procédure Two
"sait" que l'argument One
est const, de sorte qu'il s'attend à ce que le paramètre réel conserve sa valeur d'origine. L'assertion échoue.
La chaîne n'a pas été divulguée, mais ce code montre comment vous pouvez obtenir une référence dangling pour une chaîne. Arg
est un alias local de Global
. Bien que nous ayons modifié Global
, la valeur de Arg
reste intacte et, comme elle a été déclarée const, le nombre de références de la chaîne n'a pas été incrémenté lors de l'entrée dans la fonction. La réaffectation Global
a laissé tomber le nombre de références à zéro et la chaîne a été détruite. Déclarer Arg
comme var aurait le même problème; le passer en valeur permettrait de résoudre ce problème. (L'appel à UniqueString
est juste pour s'assurer que la chaîne est comptée par référence, sinon, il peut s'agir d'un littéral de chaîne non compté par référence.) Tous les types gérés par compilateur sont sensibles à ce problème; les types simples sont immunisés. La seule façon de fuir une chaîne est de la traiter autrement que comme une chaîne ou d'utiliser des fonctions de gestion de la mémoire non compatibles avec le type.Mghie's answer décrit comment traiter une chaîne comme autre chose qu'une chaîne en utilisant FillChar
pour tabuler une variable chaîne. Les fonctions de mémoire non compatibles avec le type incluent GetMem
et FreeMem
. Par exemple:
type
PRec = ^TRec;
TRec = record
field: string;
end;
var
Rec: PRec;
begin
GetMem(Rec, SizeOf(Rec^));
// Oops. Rec^ is uninitialized. This assignment isn't safe.
Rec^.field := IntToStr(4);
// Even if the assignment were OK, FreeMem would leak the string.
FreeMem(Rec);
end;
Il existe deux façons de le réparer. L'un est d'appeler Initialize
et Finalize
:
GetMem(Rec, SizeOf(Rec^));
Initialize(Rec^);
Rec^.field := IntToStr(4);
Finalize(Rec^);
FreeMem(Rec);
L'autre est d'utiliser des fonctions de type-Aware:
New(Rec);
Rec^.field := IntToStr(4);
Dispose(Rec);
AFAICS l'exemple global fonctionne pour n'importe quel type de variable, donc ce n'est probablement pas ce que Jim est après. –
Ah, vous avez raison. Mais comme une chaîne, il peut * causer * des problèmes supplémentaires, comme je suis sur le point de le démontrer quand j'édite cette réponse. –
Je pense que this aurait pu être semblable à ce que je pensais. Il est l'inverse d'une fuite de chaîne, une chaîne qui obtient recueillies au début:
var
p : ^String;
procedure InitString;
var
s, x : String;
begin
s := 'A cool string!';
x := s + '. Append something to make a copy in' +
'memory and generate a new string.';
p := @x;
end;
begin
{ Call a function that will generate a string }
InitString();
{ Write the value of the string (pointed to by p) }
WriteLn(p^); // Runtime error 105!
{ Wait for a key press }
ReadLn;
end.
Vous avez un pointeur vers un objet sur la pile qui est sorti de la portée. Qu'attendez-vous??? Que la corde soit nettoyée ou non, elle risque de souffler. –
Loren, vous vous attendez à ce que tout fonctionne exactement comme vous le vouliez, quel que soit le code que vous avez écrit! Le compilateur est psychique de nos jours, n'est-ce pas? –
Je suis d'accord avec Loren ici. La seconde fois que vous invoquez le symbole @, tous les paris sont désactivés. Vous offrez au compilateur un adieu poli à tous les mécanismes de sécurité de type et de comptage de références et prenez la mémoire brute entre vos mains. –
Une autre façon de fuir une chaîne est de le déclarer comme une variable threadvar. Voir my question pour plus de détails. Et pour la solution, voir the solution on how to tidy it.
- 1. Comment faire pour éviter les guillemets doubles dans une chaîne?
- 2. Comment faire une sous-chaîne dans jsp?
- 3. C Programmation. Comment faire une méthode pour imprimer une chaîne
- 4. Comment faire une boucle sur une chaîne multiligne dans Ruby?
- 5. Comment faire pour convertir NSInteger à une valeur (chaîne) binaire
- 6. Comment formater une chaîne pour faire partie de l'URL?
- 7. Comment faire pour que DateTimePicker affiche une chaîne vide?
- 8. Comment faire pour que Delphi 2009s (Refactor) Extract Interface fonctionne?
- 9. faire une sous-chaîne dans window.location.hash
- 10. Comment échapper des caractères dans la chaîne Delphi
- 11. Comment faire correspondre une chaîne avec une pièce optionnelle?
- 12. Comment faire une recherche regexp avec une chaîne Mandarin?
- 13. Regex pour faire correspondre "tout sauf" une chaîne
- 14. Convertir une chaîne en PAnsiChar en Delphi 2009
- 15. Comment faire pour définir une longue chaîne dans une vue de texte dans une seule ligne avec le rouleau horizontal
- 16. Comment faire pour obtenir une chaîne de la mémoire dans le script DTrace
- 17. écriture d'une chaîne à une TFileStream Delphi 2010
- 18. uuid de coupe plus loin pour faire une chaîne courte
- 19. Comment faire pour supprimer une petite partie de la chaîne dans la grande chaîne en utilisant RegExp
- 20. Comment copier une chaîne RTF dans le presse-papiers de Delphi 2009?
- 21. Comment faire pour supprimer les caractères indésirables d'une chaîne?
- 22. Comment faire pour référencer ConnectionString dans ASP.NET?
- 23. Comment faire pour dimensionner dynamiquement une applet dans une ligne
- 24. comment faire subonic convertir une chaîne à mon personnalisé Type
- 25. Comment insérer une chaîne dans une autre chaîne?
- 26. Regex pour faire correspondre la chaîne entre%
- 27. Comment faire pour mettre en avant ou lancer le navigateur dans Delphi
- 28. Comment faire pour supprimer les 0 d'une chaîne
- 29. Comment faire un .net (C#) une bibliothèque qui peut être utilisé dans delphi
- 30. Comment appeler une DLL Delphi de VB6
Cela ressemble un peu à ce que je pensais, mais ce n'est pas AV en 2009, ce que je crois est ce que vous disiez. –
Il ne fait pas d'AV car dans Delphi 2009 le "const" perd sa fonctionnalité si $ STRINGCHECKS est activé. –