CompareMem适合用来比较两个数组是否相等吗?

5

我需要比较给定数组中的所有项是否相同。

目前,我有以下代码:

Type
  TD = array [0..1] of TDateTime;

var A: TD;
    B: TD;
begin
  A[0] := Date-1;   A[1] := Date+1;
  B[0] := Date-1;   B[1] := Date+1;

  if CompareMem(@A, @B, SizeOf(TD)) then
    Showmessage('Equals')
  else
    Showmessage('Differ');

这个工作正常,但由于CompareMem是用汇编语言编写的,我目前(尚)无法理解其功能。

使用CompareMem是否是实现我想要的功能的有效方法?此外,我想知道它是否适用于所有数据类型,如字符串、整数等。


3
使用CompareMem只适用于简单数据类型。如果数据是托管类型,如对象、字符串、动态数组等,则只能比较指针值。 - LU RD
一般来说,答案是否定的,因为数组元素可能会被对齐。然而,如果您计划永远使用Borland编译器和Intel架构,那么您可以安全地这样做(这是因为Borland数组隐式地被“压缩”)。 - OnTheFly
2个回答

5

这不是用汇编语言编写的... 只有当所有内存都填满了数组项且没有间隙时,比较内存才有效。如果

1)所有数组内存都被填充了数据且没有间隙(间隙可能包含垃圾并导致假阴性)。

1.1 这应该由packed array关键字来强制实施,如果编译器不会忽略它

1.2 如果 SizeOf(A[1]) 是 2,4,8,16 等,则应该发生

但最好通过使用不同模式的 FillChar 进行单元测试来覆盖此操作-它们将模拟垃圾,然后手动填充具有匹配值的数组元素,然后使用 CompareMem 检查已擦除所有预填充的垃圾的元素。

2)数组元素仅包含简单值类型,而非引用类型。

字符、整数、双精度、短字符串、固定大小数组或者那些基于这些类型创建的记录-都是简单类型。

所有其他字符串、指针、对象、接口、动态和开放数组-都只是指向外部数据的指针,无法"按内存"比较。

您可以阅读http://docwiki.embarcadero.com/Libraries/XE2/en/System.Finalize获取更多提示。程序/函数的汇编实现也是一个好话题,因为它将涵盖不同 Delphi 数据类型的二进制表示


你的第二段代码是汇编,但你谈论的原始引用代码是Pascal。好吧,CompareMem正在执行其名称中的操作。将其制作为PByte变量并进行循环比较值并移动两个指针 - 你将拥有*功能上等效的纯Pascal代码。asm例程只是为了速度而进行了优化,但基本上你可以通过简单的循环比较所有字节来实现其等效。也许你可以从FPC获得现成的Pure Pascal实现,但这真的很简单。两个PByte指针和一个循环 - 就是这样。 - Arioch 'The
那么,使用字符串数组唯一的方法就是在循环中对每个项目执行比较? - EProgrammerNotFound
是的。您可以通过预先计算字符串的哈希值并存储哈希值+字符串而不是仅存储字符串来加快速度 - 在某些任务中,这将提高速度,但在其他任务中则不会。 - Arioch 'The
2
你对填充的讨论是有缺陷的。Delphi 数组总是紧凑的。但记录可能不是。当它们不是紧凑的时候,比较内存是行不通的,因为你会比较没有意义的填充字节。 - David Heffernan
此外,记录不是简单类型。记录是结构化类型。 - David Heffernan
显示剩余2条评论

5
CompareMem仅仅是按照字节进行比较。有两种情况下,CompareMem不能用于有效的值相等性测试:
  1. 被测试的类型包含填充。
  2. 被测试的类型是或包含引用类型。
您正在询问数组。由于数组总是紧凑排列,它们不包含填充。由于您正在比较数组值,所以问题应该集中在数组元素上。
只有当数组元素是不包含填充字节和不包含引用类型的值类型时,值比较才是适当的。
这对于所有简单值类型都适用。
对于记录,您需要检查记录是否包含引用类型。这必须是一个递归检查。记录是否包含包含引用类型的记录,依此类推。然后您必须查找填充。一旦找到填充,使用CompareMem就不合适了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接