3

我一直在使用 DUnit 作为 TDD 驱动程序在 Delphi 5 中开发一些软件,但我发现当使用 CheckEqualsMem 时它一直失败,即使在调试器中我可以看到正在比较的对象(在这种情况下是两个长字数组)是完全相同的。

在内部,CheckEqualsMem 使用 CompareMem 并发现这是返回错误的内容。

深入研究我发现,如果我使用 @ 或 Addr CompareMem 使用指向对象地址的指针调用 CompareMem,即使内存相同,也会失败,但如果我使用 PByte(来自 Windows)或 PChar,它将正确比较内存.

为什么?

这是一个例子

var
  s1  : String;
  s2  : String;
begin
  s1 := 'test';
  s2 := 'tesx';

  // This correctly compares the first byte and does not return false 
  // since both strings have in their first position
  if CompareMem(PByte(s1), PByte(s2), 1) = False then
    Assert(False, 'Memory not equal');

  // This however fails ?? What I think I am doing is passing a pointer
  // to the address of the memory where the variable is and telling CompareMem
  // to compare the first byte, but I must be misunderstanding something
  if CompareMem(@s1,@s2,1) = False then
    Assert(False,'Memory not equal');

  // Using this syntax correctly fails when the objects are different in memory
  // in this case the 4th byte is not equal between the strings and CompareMem
  // now correctly fails
  if CompareMem(PByte(s1),PByte(s2),4) = False then
     Assert(False, 'Memory not equal');
end;

正如您在评论中看到的,我来自 C 背景,所以我认为 @s1 是指向 s1 的第一个字节的指针,而 PByte(s1) 应该是同一件事,但事实并非如此。

我在这里有什么误解?@/Addr 和 PByte 有什么区别??

4

2 回答 2

6

不同之处在于它@s1是变量的地址s1,而 PByte(s1) 是变量的s1

字符串变量在内部是指向字符串数据@s1的指针,指向指针的指针也是如此。线

CompareMem(@s1,@s2,1);

比较 和 的地址的两个最低有效s1字节s2;它没有任何意义,并且与 and 引用的字符串数据Pointer(s1)无关Pointer(s2)

于 2013-03-06T19:16:36.697 回答
4

Delphi 中的字符串类型是托管类型。

字符串变量本身是一个指向内存结构的指针,它通常包含的不仅仅是纯字符信息,如代码页、引用计数等。

您可以在 DocWiki中的文章内部数据格式中找到更多信息

at运算符为您提供变量所在的内存。在这种情况下,这是存储指针的地址。甚至没有它指向的地址。例如,如果s1s2是局部方法变量,则@s1返回变量所在的堆栈中的地址。

如果要比较字符串内容,最好比较@s1[1]@s2[1]因为它指向每个字符串中的第一个字符。

如果要比较两个字符串是否指向同一个字符串(不同的 tan 具有相同的内容),您可以转换s1为指针并比较其值,或者@s1转换为指向指针的指针。

我希望这个例子可以帮助你更多地了解正在发生的事情:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

var
  s1  : String;
  s2  : String;

begin
  try
    s1 := 'test';
    //points to the same data
    s2 := s1;

    Assert(@s1<>@s2, 'They are at the same memory address, no way!');
    Assert(PPointer(@s1)^ = PPointer(@s2)^, 'They points to different address!');
    Assert(PPointer(@s1)^ = Pointer(s1), 'What jachguate says is a lie!');
    Assert(CompareMem(@s1[1], @s2[1], Length(s1) * SizeOf(Char)), 'Woa, differnet data');

    s2 := 'tesx';
    //points to different string, with partial equal content

    Assert(PPointer(@s1)^ <> PPointer(@s2)^, 'They still points to the same address!');
    Assert(CompareMem(@s1[1], @s2[1], 3 * SizeOf(Char)), 'Difference in the first 3 chars!');
    Assert(not CompareMem(@s1[1], @s2[1], 4 * SizeOf(Char)), 'No difference at all!');

    Writeln('All passed');
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

但这与您的现实世界问题不同

如果你使用动态数组,你必须记住它也是一个托管类型,所以你必须更好地获取每个数组的第一个元素的地址来进行比较。

如果不是这样,我建议您在问题中使用真实代码来显示您的真实问题,而不是不同的问题。

于 2013-03-06T19:18:10.373 回答