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 может изменить содержимое строки и вызывающий увидеть изменения.