Чтение и назначение параметра типа void - PullRequest
1 голос
/ 09 июля 2009

Я написал два метода с параметром типа void:

procedure Method1(const MyVar; size: cardinal);
var
  Arr: array of byte;
begin
  SetLength(Arr, size);
  {now copy the data from MyVar to Arr, but how?}  
end;

procedure Method2(var MyVar; size: cardinal);
var
  Arr: array of byte;
begin
  SetLength(Arr, size);
  {return the data from the array, but how?}
end;

В первом я хотел бы получить доступ к MyVar в виде массива байтов. Во втором я хотел бы скопировать данные из локального массива Arr в MyVar. Поэтому я использовал функцию CopyMemory (), но с ней что-то не так.

Если я использую следующее во втором методе, это нормально, если метод2 вызывается с массивом в качестве его параметра (Method2 (Pointer (MyString) ^, Length (MyString)) или Method2 (Pointer (MyArray), Длина (MyArray))).

CopyMemory(Pointer(MyVar), Pointer(Arr), size);

Если я вызываю Method2, например, с целочисленным параметром (Method2 (MyInteger, SizeOf (MyInteger))), он не будет работать должным образом. В этом случае CopyMemory () должна вызываться следующим образом:

CopyMemory(@MyVar, Pointer(Arr), size);

Как правильно вернуть данные из Method2, не зная, простой ли это тип (или запись) или массив? Ситуация будет аналогичной в Method1, но здесь я должен был бы использовать

CopyMemory(Pointer(Arr), Pointer(MyVar), size);

в случае массивов и

CopyMemory(Pointer(Arr), @MyVar, size);

в случае простых типов.

Что я могу сделать с этим, когда я не знаю, что такое параметр MyVar?

Ответы [ 2 ]

6 голосов
/ 09 июля 2009

В Delphi нет такого типа пустоты. То, на что вы ссылаетесь, называется нетипизированный параметр .

Нетипизированный параметр - это всегда фактическая вещь , а не указатель на то, что вы должны использовать. Следовательно, правильный способ использования CopyMemory с таким параметром - применить к нему оператор @, например:

CopyMemory(@MyVar, @Arr[0], size);

Обратите внимание, я также изменил способ передачи второго параметра. Лучше, если вы не полагаетесь на тот факт, что динамический массив действительно является указателем на первый элемент. Если вам нужен указатель на первый элемент, просто скажите об этом явно, как я сделал здесь.

Ваша путаница связана с проведенным вами тестом, в котором параметр использовался, как если бы он был указателем, и этот тест, похоже, работал. Однако я сомневаюсь в правильности этого теста. Когда вы сказали Pointer(MyString)^, то, что у вас было, было первым символом строки. Когда вы тогда сказали Pointer(MyVar) внутри функции, вы приводили этот тип в указатель, который был недопустимым. Если ваша программа оказалась работоспособной, то это было только случайно; Ваш код был неправильным.

Лучший совет, который я могу дать, это не набирать вещи, если только вам это не нужно. И когда вы это сделаете, пожалуйста, будьте уверены, что вещь, которую вы печатаете, действительно имеет этот тип. В вашем случае вам не нужно ничего приводить к типу, прежде чем передать его как нетипизированный параметр. Вы можете позвонить Method1 так:

Method1(MyString[1], Length(MyString) * Sizeof(Char));

(я умножаю на SizeOf(Char), поскольку не знаю, есть ли у вас Delphi 2009 или нет.)

Кроме того, избегайте нетипизированных параметров. Одна из замечательных вещей, которую может сделать компилятор для обеспечения правильности программы, - обеспечить безопасность типов, но когда вы убираете типы, компилятор больше не может вам помочь.

4 голосов
/ 09 июля 2009

Ваша проблема в том, что вы передаете либо данные, либо указатель на данные в метод 1/2. Вы должны всегда передавать сами данные. Может быть, вы просто забыли, что динамический массив сам по себе является указателем? Вы не должны передавать A или Pointer (A) в ваших методах (здесь A - динамический массив). Пройдите A [0] или Указатель (A) ^.

procedure Method1(const MyVar; size: cardinal);
var
  Arr: array of byte;
begin
  SetLength(Arr, size);
  CopyMemory(Pointer(Arr), @MyVar, Size);
end;

procedure Method2(var MyVar; size: cardinal);
var
  Arr: array of byte;
begin
  SetLength(Arr, size);
  Arr[0] := 1;
  Arr[1] := 2;
  Arr[2] := 3;
  Arr[3] := 4;
  CopyMemory(@MyVar, Pointer(Arr), Size);
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  I: Integer;
  A: array of Integer;
begin
  I := $01020304;
  Method1(I, 4); // we pass data itself, not the pointer to it.
  Method2(I, 4);
  SetLength(A, 2);
  A[0] := 0;
  A[1] := $01020304;
  Method1(A[0], Length(A) * SizeOf(A[0])); // note, that we pass data, not pointer
  Method2(A[0], Length(A) * SizeOf(A[0])); // A or Pointer(A) is a pointer to array's data
end;

Если A - динамический массив:

  1. A [0] совпадает с Pointer (A) ^ и представляет данные массива.
  2. @ A [0] - это то же самое, что Pointer (A) или просто A и представляет сам массив, который является указателем на его данные (и некоторую техническую информацию о отрицательных смещениях).

Если A - статический массив:

  1. A [0] совпадает с A и представляет сам массив, который является данными массива.
  2. @ A [0] совпадает с @A и представляет указатель на массив.
  3. Указатель (A) или Указатель (A) ^ не имеют смысла.

Обратите внимание, что Arr в Method1 / 2 также является динамическим массивом, поэтому мы приводим его к указателю (CopyMemory запрашивает указатели, а не данные). Если мы хотим использовать процедуру Move (которая запрашивает данные), мы должны вместо этого написать Pointer (A) ^.

...