Копия Delphi Memory с записью в другую запись - PullRequest
4 голосов
/ 23 февраля 2012

У меня проблемы с логикой.Я понятия не имею, как скопировать запись в другую запись в Delphi.

TypeA = record
  value1 : word;
  value2 : word;
  value3 : word;
  end;

TypeB = record
  b1 : byte;
  b2 : byte;    
  end;

У меня есть две записи TypeA и TypeB.Например, я обнаружил, что данные в записи TypeA принадлежат записям TypeB. Примечание. Тип A имеет большую длину данных.

Вопрос. Как скопировать память TypeA и поместить ее в запись TypeB?

CopyMemory(@TypeA, @TypeB, Length(TypeB))

Когда я попробовал CopyMemory и получил ошибку (Несовместимые типы) .

PS: я не хочу копировать или назначать его, как показано ниже.

TypeB.b1: = TypeA.value1 && $ FF;

TypeA и TypeB - только примеры записей.В большинстве случаев записи TypeA и TypeB могут содержать несколько записей, и будет сложнее выделить форму TypeA и присвоить записи TypeB.

Заранее спасибо

---- Добавлениевопрос:

Есть ли способ скопировать Delphi-запись в байтовый массив и как?Если есть,

  • Запись типа A в массив байтов
  • Массив байтов в тип B

Будет ли работать эта логика?

Ответы [ 7 ]

7 голосов
/ 23 февраля 2012
CopyMemory(@a, @b, SizeOf(TypeB))

, если a имеет тип TypeA, а b имеет тип TypeB.

5 голосов
/ 23 февраля 2012

Вариант записи:

TypeA = packed record
  value1 : word;
  value2 : word;
  value3 : word;
  end;

TypeB = packed record
  b1 : byte;
  b2 : byte;
  end;

TypeAB = packed record
  case boolean of
    false:(a:TypeA);
    true:(b:TypeB);
end;
..
..
var someRec:TypeAB;
    anotherRec:TypeAB;
..
..
  anotherRec.b:=someRec.b
3 голосов
/ 23 февраля 2012

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

Если вы пытаетесь получить первые два байта одного элемента записи, попробуйте использовать вариант записи (на языке C, объединение.) Это раздел записи, который может быть адресовано несколькими способами, и здесь вы хотите обратиться к нему как к слову или серии байтов.

Попробуйте что-то вроде этого (не проверено, только что напечатано в редакторе SO):

type
  TTwoBytes : packed record
    ByteA, ByteB : Byte;
  end;

  TValueRecord : packed record:
    case Boolean of
      True: (Value: SmallInt);
      False: (ValueBytes : TTwoBytes);
    end;
  end;

  TMyRecord = packed record
    Value1, Value2, Value3 : TValueRecord;
  end;

Тогда для кода:

var
  MyRecord: TMyRecord;
  MyBytes: TTwoBytes;
begin
  MyRecord := ...; // Fill it with data here
  // Access the words / smallints by something like: MyRecord.Value1.Value
  MyBytes := MyRecord.ValueBytes; // The key bit: type safe assignment
  // Do something with MyBytes.ByteA or MyBytes.ByteB
end;

Что это дает вам лучше, чем прямое копирование памяти?

  • Это безопасно: если у вас есть более сложные записи, содержащие строки, интерфейсы и т. Д., Он все равно будет работать, не нарушая счетчика ссылок.
  • Это безопасный тип: нет прямого копирования памяти: вы и компилятор знаете, что у вас есть и используете.
  • Это канонично: это, вероятно, «способ Дельфи». (Хотя есть несколько способов структурировать записи - если вы посмотрите в истории ответов, у меня изначально был худший вариант. Также я думаю, что синтаксис «case ... of» для этого глуп, но это другое обсуждение ... :))

Некоторые заметки:

3 голосов
/ 23 февраля 2012

для копирования записи A в запись B есть хороший способ - перегрузка оператора; в этом случае вы можете просто перегрузить неявные и явные операции и написать код, например b := a:

program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses System.SysUtils;

type
    TTypeA = record
      value1 : integer;
      value2 : integer;
      value3 : integer;
    end;

    TTypeB = record
      b1 : byte;
      b2 : byte;
      class operator Implicit(value : TTypeA):TTypeB;
    end;

class operator TTypeB.Implicit(value: TTypeA): TTypeB;
begin
    result.b1 := Hi(value.value1);
    result.b2 := Lo(value.value1);
end;


var a : TTypeA;
    b : TTypeB;
begin
    b := a;
end.

Вы можете использовать TBytesStream для копирования записи в байтовый массив:

var a : TTypeA;
    bs : TBytesStream;
    bArr : TArray<byte>;//array of byte;
begin
    bs := TBytesStream.Create();
    bs.Write(a, sizeof(a));
    bArr := bs.Bytes;
end;

или просто установите размер байтового массива равным sizeof(A), а затем copyMemory

3 голосов
/ 23 февраля 2012
procedure CopyAtoB( const A: TypeA; var B: TypeB);
begin
// Assume A is bigger than B.
Move( A, B, SizeOf( TypeB))
end;

или (с математическим блоком)

procedure CopyAtoB( const A: TypeA; var B: TypeB);
begin
// No assumptions about A, B sizes.
FillChar( B, SizeOf( B), 0);
Move( A, B, Min( SizeOf( TypeA), SizeOf( TypeB)))
end;
2 голосов
/ 23 февраля 2012

Вы можете не использовать CopyMemory () (модуль Windows) следующим образом:

type
  PTypeA = ^TTypeA;
  TTypeA = record
    value1 : word;
    value2 : word;
    value3 : word;
  end;
  PTypeB = ^TTypeB;
  TTypeB = record
    b1 : byte;
    b2 : byte;
  end;
var
  A: TTypeA = (value1 : 11; value2 : 22; value3 : 33);
  B: TTypeB;
  B1: TTypeB;
  C: {packed?} array of Byte;
begin
  Assert(SizeOf(TTypeA) >= SizeOf(TTypeB));
  //...
  B:= PTypeB(@A)^;
  //...
  SetLength(C, SizeOf(A));
  // TTypeA record to Byte Array
  PTypeA(@C[0])^:= A;
  // Byte Array to TTypeB
  B1:= PTypeB(@C[0])^
end;
0 голосов
/ 23 февраля 2012

Используйте MOVE вместо CopyMemory, если вы хотите просто скопировать байты из одного места в другое.

И чтобы получить размер записи, используйте SizeOf вместо Length.

...