In Delphi, string is a reference type that normally acts like a value type. It is allocated on the heap (not the stack like most value types) and features automatic reference counting and copy-on-write semantics.
To understand what this means, consider how normal value types, e.g. an Integer, behave when passed as a parameter to a procedure:
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;
The var parameter in PassByReferenceInOut is equivalent to the C convention of passing a pointer to the argument.
The same semantics apply to string parameter passing, but there is a subtle difference in the internal representation of the values:
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;
If you want to make sure a procedure operates on its own copy of the string value, use the UniqueString procedure, e.g.:
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;