Как вы передаете массив по ссылке в Delphi?
Я уже прочитал о передаче по ссылке и так
procedure test(var x:integer);
begin
x:=x+5;
end;
поэтому приведенный выше код обновляется 5 по ссылке. Я предположил, что если бы я обновлял массив по ссылке, я мог бы объявить var X: array of blah... с некоторыми связанными ошибками и просто хотел узнать, должен ли я использовать тип данных для указателя на данные или указатель всегда int... просто так я знаю, проблема в том, как я делаю передачу по ссылке или что-то еще в моем коде.
2 ответа
Если вы передадите динамический массив как параметр без переменной, компилятор сделает копию.
Небольшой пример кода ниже демонстрирует это, отображая 37/42 в заголовке формы.
procedure IncArray1(data: array of integer);
var i : integer;
begin
for i := Low(data) to High(data) do
data[i] := data[i] + 5;
end;
procedure IncArray2(var data: array of integer);
var i : integer;
begin
for i := Low(data) to High(data) do
data[i] := data[i] + 5;
end;
procedure TForm8.FormCreate(Sender: TObject);
var
data: array of integer;
begin
SetLength(data, 1);
data[0] := 37;
IncArray1(data);
Caption := IntToStr(data[0]);
IncArray2(data);
Caption := Caption + '/' + IntToStr(data[0]);
end;
Если мы посмотрим на сгенерированный ассемблерный код, IncArray1 начинается с
004552B4 8BCA mov ecx,edx
004552B6 85C9 test ecx,ecx
004552B8 7807 js $004552c1
004552BA 8B1C88 mov ebx,[eax+ecx*4]
004552BD 49 dec ecx
004552BE 53 push ebx
004552BF 79F9 jns $004552ba
004552C1 8BC4 mov eax,esp
Этот код копирует исходный массив в стек и устанавливает eax по адресу первого элемента (= адрес, сохраненный в указателе стека после последнего нажатия). Стек увеличивается, поэтому код начинается с последнего элемента (edx содержит High (data), когда вызывается IncArray1) и повторяется (чтение элемента; push-элемент; декремент индекса), пока не дойдет до элемента 0.
IncArray2 не содержит такого кода. Вызывающая сторона сохраняет адрес данных в регистре eax перед вызовом IncArray2, а IncArray2 просто использует этот адрес.
Если вы по какой-либо причине не хотите использовать 'var', вы можете передать адрес данных вашему методу. Но поскольку вы не можете использовать синтаксис "data: ^ массив целых чисел" при объявлении параметров, вам придется объявить тип для ваших данных. И вам придется использовать "данные ^" вместо "данные" везде в методе.
type
TData = array of integer;
PData = ^TData;
procedure IncArray(data: PData);
var i : integer;
begin
for i := Low(data^) to High(data^) do
data^[i] := data^[i] + 5;
end;
procedure TForm8.FormCreate(Sender: TObject);
var
data: TData;
begin
SetLength(data, 2);
data[0] := 37;
IncArray(@data);
Caption := IntToStr(data[0]);
end;
Протестировано с Delphi 2007.
Ответ Габра верен, но ключевой момент достаточно глубоко спрятан, и я выделю его отдельным постом:
Определите ваши типы в первую очередь! В этом конкретном случае компилятор принял массив целых чисел, но это только потому, что он имеет особое значение, и это НЕ то, что вы ожидали. Любая другая попытка определить тип в определении процедуры просто потерпела бы неудачу.
В отличие от C, если вы хотите, чтобы две вещи были совместимы по присваиванию, вы должны объявить их как один и тот же тип, а не просто два одинаковых типа:
Var
A : Array [1..4] of Integer;
B : Array [1..4] of Integer;
Begin
A := B;
Не будет компилироваться Скорее:
Type
Array4 = array [1..4] of Integer;
Var
A : Array4;
B : Array4;
Begin
A := B;
и компилятор делает то, что вы ожидаете.