Delphi paramètres de passage par référence ou par valeur / copie
Contexte 1
var text:String;
text:='hello';
myFunc(text);
Contexte2
function myFunc(mytext:String);
var textcopy:String;
begin
textcopy:=mytext;
end;
myFunc
sur le Contexte2 a été appelée à partir de la Contexte1, la variable locale mytext
est-ce que pointer vers un souvenir en dehors du contexte 2? ou mytext
avoir leur propre espace mémoire à l'intérieur de la portée, et rempli/copié avec le même contenu de l' <!--5? Je suis probablement en train de rater quelque chose de vraiment basique, parce que je reçois un access violation
erreur.
il y a un moyen de spécifier explicitement si une fonction devrait recevoir des paramètres par référence ou par valeur, copier puis comme en C? Je ne suis pas sûr de savoir comment je le fais.
3 réponses
la gestion de la mémoire pour les chaînes Delphi est un peu inhabituelle. Après l'appel de myFunc(text)
, et d'attribuer textcopy := mytext
, les trois variables (text
, mytext
et textcopy
) sera pointant vers la même adresse, celle de la chaîne d'origine.
Mais dès que vous utilisez l'une de ces variables pour modifier la chaîne, Delphi clone la chaîne derrière les scènes, et vos modifications sont appliquées à la copie. Les deux autres variables pointent toujours vers l'original, restent inchangés. Ainsi, tout changement apporté dans le Contexte 2 ne sera pas vu dans le Contexte 1 - ce" copy-on-write " mécanique donne effectivement vous pass-by-value sémantique. Toutes ces chaînes sont comptées par référence, et seront libérées automatiquement une fois que toutes les références seront hors champ.
cependant, il y a une exception. Si vous accédez à la chaîne en utilisant des pointeurs, plutôt que des opérations de chaîne, vous contournerez l'étape de copie et vos modifications affecteront l'original. Vous contournerez également le logique de comptage de référence, et peut-être finir avec un pointeur vers un bloc de mémoire désactivé. C'est peut-être la raison de votre violation de l'accès, mais je ne pourrais pas le dire sans plus de détails / plus de code.
si vous voulez passer de référence, déclarez votre fonction comme myFunc(var mytext: String)
. Si vous voulez forcer Delphi à copier la chaîne, au lieu d'attendre qu'elle soit modifiée, vous pouvez utiliser System.UniqueString
.
dans Delphi, string est un type de référence qui agit normalement comme un type de valeur. Il est alloué sur le tas (pas la pile comme la plupart des types de valeur) et dispose d'un comptage de référence automatique et d'une sémantique de copie à l'écriture.
pour comprendre ce que cela signifie, considérons comment les types de valeurs normales, par exemple un entier, se comportent lorsqu'ils sont passés comme paramètre à une procédure:
var
gValue: Integer;
procedure PassByValue(aValue: Integer);
begin
// Here @gValue <> @aValue
aValue := aValue + 2;
// Here @gValue <> @aValue
end;
procedure PassByRefrenceInOut(var aValue: Integer);
begin
// Here @gValue = @aValue
aValue := aValue + 2;
// Here @gValue = @aValue
end;
procedure CallProcedures;
begin
gValue := 0;
PassByValue(gValue);
// gValue is still 0
PassByReferenceInOut(gValue);
// gValue is 2
end;
le paramètre var dans PassByReferenceInOut est équivalent à la convention C de passer un pointeur à argument.
la même sémantique s'applique au passage du paramètre string, mais il y a une différence subtile dans la représentation interne des valeurs:
var
gValue: string;
procedure PassByValue(aValue: string);
begin
// Here PChar(gValue) = PChar(aValue) <<<<
aValue := aValue + '2';
// Here PChar(gValue) <> PChar(aValue)
end;
procedure PassByRefrenceInOut(var aValue: string);
begin
// Here PChar(gValue) = PChar(aValue)
aValue := aValue + '2';
// Here PChar(gValue) = PChar(aValue)
end;
procedure CallProcedures;
begin
gValue := '';
PassByValue(gValue);
// gValue is still ''
PassByReferenceInOut(gValue);
// gValue is '2'
end;
si vous voulez vous assurer qu'une procédure fonctionne sur sa propre copie de la valeur string, utilisez la procédure UniqueString, par exemple:
procedure PassByValue(aValue: string);
begin
// Here PChar(gValue) = PChar(aValue)
UniqueString(aValue);
// Here PChar(gValue) <> PChar(aValue)
aValue := aValue + '2';
// Here PChar(gValue) <> PChar(aValue)
end;
dans Delphi pour passer par référence, vous ajoutez explicitement le mot-clé var:
procedure myFunc(var mytext:String);
cela signifie que myFunc peut modifier le contenu de la chaîne et faire voir les changements à l'appelant.