2

私はこの簡単なコードを持っています

procedure TForm2.btn1Click(Sender: TObject);
var s : TStringList;

  function compare(s : TStringList; i1, i2 : integer) : integer;
  begin
    result := CompareText(s[i1], s[i2]);
  end;

begin
  s := TStringList.Create;
  try
    s.add('s1');
    s.add('s2');
    s.add('s3');
    s.CustomSort(@compare);
  finally
    s.free;
  end;
end;

32 ビットとしてコンパイルすると期待どおりに動作しますが、64 ビットを使用するとアクセス違反が発生します。関数比較で 64 ビット版の場合、s = nil. i2 = some random value;

compare関数の外に関数を抽出すると、Win64ターゲットでも期待どおりに機能しbtn1Clickます。

System.Classes のバグですか、修正する方法はありますか?

4

1 に答える 1

4

ネストされたローカル関数は、手続き型変数に割り当てるべきではありません (特に、手続き型変数のパラメーターとして渡すべきではありません)。

http://docwiki.embarcadero.com/RADStudio/XE4/en/Procedural_Types - 「ネストされた」を検索してください。

理由は簡単です。ローカル関数は、上位関数 (親) のスタック フレームすべてにアクセスできるようにスタックを配置する必要があります。ただし、これらのスタック フレームは、親関数のすべてのチェーンを 1 つずつ事前に呼び出さずにそれらの関数にジャンプした場合には存在できません。そして、その「思いがけない」ダイブは、この特定の呼び出しチェーンに気付いていない幹部に外部のアドレスを渡すときにまさに起こることです. これは、VMT が親クラスへのリンクを持たず、Self が適切な VMT を指していない状態で、オブジェクトの仮想メソッドを呼び出すようなものです。

バグは、そのようなコードが win64 で機能しないことではありません。

バグは、win32 でも win64 でもコンパイルできることです。

Delphi がバグを修正するとcompare、適切なグローバル関数を作成しない限り、そのようなコードはコンパイルされなくなります。

于 2013-07-22T09:13:50.470 に答える