Reputation: 818
I've been developing some software in Delphi 5 using DUnit as a driver for TDD but I found that when using CheckEqualsMem it kept failing even though in the debugger I could see that the objects being compared (two arrays of longwords in this case) were identical.
Internally, CheckEqualsMem uses CompareMem and found that this was what was returning false.
Delving a bit deeper I found that if I call CompareMem with a pointer to the address of the objects using @ or Addr CompareMem fails even when the memory is identical, but if I use PByte (from windows) or PChar it will compare the memory properly.
Why?
Here is an example
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;
As you can see in the comments, I come from a C background, so I thought that @s1 is a pointer to the first byte of s1 and PByte(s1) should be the same thing, but its not.
What am I misunderstanding here? What is the difference between @ / Addr and PByte ??
Upvotes: 3
Views: 4866
Reputation: 17203
the string type in Delphi is a managed type.
The string variable itself is a pointer to a structure in memory, which typically contains more than the pure character information, like the code page, reference count, etc.
You can find more information in the article Internal Data Formats in the DocWiki
The at
operator gives you the memory where the variable resides. This is, in this case, the address where the pointer is stored. Not even the address where it points to. If s1
and s2
are local method variables, for example, @s1
returns the address in the stack where the variable lives.
If you want to compare the strings contents, better compare @s1[1]
against @s2[1]
, since that points to the first character in each string.
If you want to compare if both strings points to the same string (different tan having the same content), you can cast s1
as a pointer and compare its values, or cast @s1
as a pointer to pointer.
I hope this example helps you to get more idea of what's going on:
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.
If you use dynamic arrays, you have to remember it is also a managed type, so you must better get the address of the first element of each array to compare.
If that's not the case, I advise you to use real code in your question to show your real problem and not a different one.
Upvotes: 4
Reputation: 27493
The difference is that @s1
is address of the variable s1
while PByte(s1) is value of the variable s1
.
A string variable internally is a pointer to a string data, so @s1
is a pointer to pointer. The line
CompareMem(@s1,@s2,1);
compares two least significant bytes of the addresses of s1
and s2
strings; it makes no sense and have no relation to the string data referenced by Pointer(s1)
and Pointer(s2)
.
Upvotes: 6