3

DUnit を TDD のドライバーとして使用して Delphi 5 でいくつかのソフトウェアを開発していましたが、CheckEqualsMem を使用すると、デバッガーで比較されているオブジェクト (この場合はロングワードの 2 つの配列) が比較されていることを確認できたにもかかわらず、失敗し続けることがわかりました。同一。

内部的には、CheckEqualsMem は CompareMem を使用し、これが false を返していることを発見しました。

もう少し深く掘り下げると、 @ または 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であるのに対し、PByte(s1) は変数のです。s1s1

文字列変数は、内部的には文字列データへの@s1ポインターであり、ポインターへのポインターでもあります。この線

CompareMem(@s1,@s2,1);

s1s2文字列のアドレスの最下位 2 バイトを比較します。意味がなく、Pointer(s1)および で参照される文字列データとは関係ありませんPointer(s2)

于 2013-03-06T19:16:36.697 に答える
4

Delphi の文字列型はマネージド型です。

文字列変数自体はメモリ内の構造体へのポインタであり、通常はコード ページや参照カウントなどの純粋な文字情報以上のものを含んでいます。

詳細については、DocWiki の内部データ形式に関する記事を参照してください。

演算子はat、変数が存在するメモリを提供します。この場合、これはポインタが格納されているアドレスです。それが指すアドレスでさえありません。たとえばs1、 とs2がローカル メソッド変数である場合@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 に答える