Как вы передаете массив по ссылке в Delphi? - PullRequest
11 голосов
/ 03 апреля 2009

Я уже читал о передаче по ссылке и так

procedure test(var x:integer);
begin
  x:=x+5;
end;

, поэтому приведенный выше код обновляется 5 по ссылке. Я предположил, что, если бы я обновлял массив по ссылке, я мог бы объявить var X: массив бла ..., имеющий некоторые связанные ошибки, и просто хотел узнать, должен ли я использовать тип данных для указателя на данные или если указатель всегда int ... просто так я знаю, что проблема заключается в том, как я делаю передачу по ссылке или что-то еще в моем коде.

Ответы [ 2 ]

24 голосов
/ 03 апреля 2009

Если вы передадите динамический массив как параметр без переменной, компилятор сделает копию.

Небольшой пример кода ниже демонстрирует это, отображая 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.

6 голосов
/ 03 апреля 2009

Ответ Габра верен, но ключевой момент достаточно глубоко спрятан, и я выделю его отдельным постом:

Сначала определите ваши типы! В этом конкретном случае компилятор принял массив целых чисел, но это только потому, что он имеет особое значение и это НЕ , что вы ожидали. Любая другая попытка определить тип в определении процедуры просто потерпела бы неудачу.

В отличие от C, если вы хотите, чтобы две вещи были совместимыми по назначению, вы должны объявить их как тип SAME , а не просто два одинаковых типа:

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;

и компилятор делает то, что вы ожидаете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...