【问题标题】:Delphi Memory copy with Record to another Record带记录的德尔福内存复制到另一个记录
【发布时间】:2012-02-23 03:35:43
【问题描述】:

我的逻辑有问题。我不知道如何将记录复制到 Delphi 中的另一条记录。

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

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

我有两条记录 TypeA 和 TypeB。例如,我发现TypeA记录中的数据属于TypeB记录。 注意:TypeA 的数据长度更长。

问题:如何将TypeA内存复制到TypeB记录中?

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

当我尝试 CopyMemory 并收到错误 (不兼容的类型)

PS:我不想像下面这样复制或分配它。

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

TypeA 和 TypeB 只是示例记录。大多数情况下,记录 TypeA 和 TypeB 可能包含多个记录,并且它 将更难分配表单 TypeA 并分配给 TypeB 记录。

提前致谢

----加题:

有没有办法将 Delphi 记录复制到字节数组以及如何?如果有的话,

  • TypeA 记录到字节数组
  • B 型字节数组

这个逻辑行得通吗?

【问题讨论】:

  • 你的问题毫无意义。您想将一条记录的一半复制到另一条记录中?您可能需要将多个半记录复制到多个其他记录中?你需要澄清你的问题,因为现在它似乎很奇怪(而且无法回答,因为不清楚你想做什么)。
  • 感谢您的评论。我也不清楚自己如何获得逻辑。我得到的信息是 TypeA 记录数据属于 TypeB 记录数据。放错地方了我想恢复数据并保存在TypeB中。
  • 尝试Move() 而不是CopyMemory() 例如:Move(TypeA, TypeB, SizeOf(TypeB));
  • @Remy:为什么? MoveMemoryCopyMemory 都实现为 Move(Source^, Destination^, Length);
  • 你有两个问题。您正在尝试获取类型的地址。您需要声明一些变量。而且你需要使用 sizeof 而不是 length。

标签: delphi memory copy record


【解决方案1】:
CopyMemory(@a, @b, SizeOf(TypeB))

如果aTypeA 类型,而bTypeB 类型。

【讨论】:

  • 我对此表示赞同,但如果有一些解释会更好。否则它看起来就像魔术一样。
  • 它还需要一个警告:如果TypeB 包含托管类型的任何字段,这会干扰Delphi 的使用计数器,并且可能会导致无法追踪的AV
【解决方案2】:

变体记录:

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】:
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;

【讨论】:

    【解决方案4】:

    将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

    【讨论】:

    • 我喜欢这个 - 比我下面使用变体记录的答案更现代的 Delphi。
    【解决方案5】:

    所有其他答案都是直接复制内存的变体,但我认为这是错误的方法。它是低级的,您使用的是高级语言,很容易出错,如果您的记录包含托管数据类型,则存在风险。

    如果您要获取记录的一个元素的前两个字节,请尝试使用变体记录(在 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;
    

    与直接复制内存相比,这给您带来了什么好处?

    • 它是安全的:如果您有更复杂的记录,包含字符串、接口等,它仍然可以在不破坏引用计数的情况下工作。
    • 它是类型安全的:没有直接复制内存:您和编译器都知道您拥有和正在使用什么。
    • 这是规范的:这可能是“Delphi 方式”。 (虽然有几种方法可以构建记录 - 如果您查看答案历史记录,我最初的情况更糟。另外我认为 'case ... of' 的语法很愚蠢,但这是另一个讨论...... :))

    一些注意事项:

    【讨论】:

    • '所有其他答案都是直接复制内存的变体'-不,它们不是!
    • @MartinJames:他们不是现在,而是我开始写这篇文章的时候:)
    【解决方案6】:

    您可以省略 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;
    

    【讨论】:

      【解决方案7】:

      如果您想简单地将字节从一个地方复制到另一个地方,请使用 MOVE 而不是 CopyMemory。

      要获取记录的大小,请使用 SizeOf 而不是 Length。

      【讨论】:

      • MoveMemoryCopyMemory 都实现为 Move(Source^, Destination^, Length);
      • MoveMemory()CopyMemory() 需要 Windows 单元,这是特定于平台的。 Move() 位于 System 单元中,适用于所有平台。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-05-06
      • 1970-01-01
      • 2014-07-08
      • 1970-01-01
      • 1970-01-01
      • 2011-01-31
      • 2012-09-25
      相关资源
      最近更新 更多