Delphi передача параметров по ссылке или по значению / копии
контекст 1
var text:String;
text:='hello';
myFunc(text);
Context2
function myFunc(mytext:String);
var textcopy:String;
begin
textcopy:=mytext;
end;
myFunc
на Context2 был вызван из Context1, локальная переменная mytext
указывает на память вне Context2? или mytext
имеют собственное пространство памяти внутри области и заполнены / скопированы с тем же содержанием text
? Я, вероятно, пропустил что-то действительно основное, потому что я получаю access violation
ошибка.
есть любой способ явно указать, должна ли функция получить параметры по ссылке или по значению, копируя то, как в C? Я не уверен, как я это делаю.
3 ответов
управление памятью для строк Delphi немного необычно. После вызова myFunc(text)
и назначить textcopy := mytext
, все три переменные (text
, mytext
и textcopy
) будет указывать на тот же адрес, что и в исходной строке.
но как только вы используете одну из этих переменных для внесения изменений в строку, Delphi клонирует строку за кулисами, и ваши изменения применяются к копии. Две другие переменные по-прежнему указывают на оригинал, поэтому они оставаться неизменным. Таким образом, любые изменения, сделанные в контексте 2, не будут видны в контексте 1 - этот механик "копировать-на-запись" эффективно дает вам семантику пропуска по значению. Все эти строки подсчитываются по ссылкам и будут освобождены автоматически, как только все ссылки выйдут за пределы области.
однако есть исключение. Если вы обращаетесь к строке с помощью указателей, а не строковых операций, вы обойдете шаг копирования, и ваши изменения повлияют на оригинал. Вы также обойдете логика подсчета ссылок и потенциально заканчивается указателем на освобожденный блок памяти. Это может быть причиной вашего нарушения, но я не могу сказать без более подробной информации / кода.
если вы хотите передать ссылку, объявите свою функцию как myFunc(var mytext: String)
. Если вы хотите заставить Delphi скопировать строку, вместо того, чтобы ждать ее изменения, вы можете использовать System.UniqueString
.
в Delphi string является ссылочным типом, который обычно действует как тип значения. Он выделяется в куче (а не в стеке, как большинство типов значений) и имеет автоматический подсчет ссылок и семантику копирования при записи.
чтобы понять, что это значит, рассмотрим, как обычные типы значений, например целое число, ведут себя при передаче в качестве параметра процедуре:
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;
параметр var в PassByReferenceInOut эквивалентен соглашению C о передаче указателя на аргумент.
та же семантика применяется к передаче параметров строки, но есть тонкая разница во внутреннем представлении значений:
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;
Если вы хотите убедиться, что процедура работает с собственной копией строкового значения, используйте процедуру UniqueString, например:
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;
в Delphi для передачи по ссылке вы явно добавляете ключевое слово var:
procedure myFunc(var mytext:String);
Это означает, что myFunc может изменить содержимое строки и вызывающий увидеть изменения.