23

正しいと思われる Delphi コードの予期しないアクセス違反がいくつかありますが、コンパイルが間違っているようです。私はそれを減らすことができます

procedure Run(Proc: TProc);
begin
  Proc;
end;

procedure Test;
begin
  Run(
    procedure
    var
      S: PChar;

      procedure Nested;
      begin
        Run(
          procedure
          begin
          end);
        S := 'Hello, world!';
      end;

    begin
      Run(
        procedure
        begin
          S := 'Hello';
        end);
      Nested;
      ShowMessage(S);
    end);
end;

私に起こることは、それS := 'Hello, world!'が間違った場所に保存されていることです。そのため、アクセス違反が発生するか、ShowMessage(S)"Hello" が表示されます (場合によっては、匿名プロシージャの実装に使用されたオブジェクトを解放するときにアクセス違反が発生します)。

私は Delphi XE を使用しており、すべてのアップデートがインストールされています。

これがどこで問題を引き起こすかをどのように知ることができますか? 匿名の手続きを避けるためにコードを書き直す方法は知っていますが、どのような状況でそれらが間違ったコードにつながるかを正確に把握するのに苦労しているため、どこでそれらを避けるべきかわかりません。

これが Delphi の以降のバージョンで修正されているかどうかを知りたいのですが、興味深いだけでなく、現時点ではアップグレードはオプションではありません。

QC では、最新のレポートで同様の#91876を見つけることができますが、これは Delphi XE で解決されています。

更新

AlexSC のコメントに基づいて、わずかに変更します。

...

      procedure Nested;
      begin
        Run(
          procedure
          begin
            S := S;
          end);
        S := 'Hello, world!';
      end;

...

動作します。

生成されたマシンコード

S := 'Hello, world!';

失敗したプログラムでは

ScratchForm.pas.44: S := 'Hello, world!';
004BD971 B89CD94B00       mov eax,$004bd99c
004BD976 894524           mov [ebp+$24],eax

一方、正しいバージョンは

ScratchForm.pas.45: S := 'Hello, world!';
004BD981 B8B0D94B00       mov eax,$004bd9b0
004BD986 8B5508           mov edx,[ebp+$08]
004BD989 8B52FC           mov edx,[edx-$04]
004BD98C 89420C           mov [edx+$0c],eax

失敗したプログラムで生成されたコードはS、コンパイラによって生成されたクラスに移動されたことを認識してい[ebp+$24]ません。ネストされたメソッドの外部ローカル変数にアクセスする方法ローカル変数へのアクセス方法。

4

2 に答える 2

1

全体のアセンブラー コード全体 (手順テスト) を確認せずに、投稿したスニペットのみを想定すると、失敗したスニペットではポインターのみが移動され、正しいバージョンではデータも移動された可能性があります。

したがって、 S:=S または S:='' により、コンパイラは独自に参照を作成し、メモリを割り当てることさえできるようです。

また、S:=S または S:='' なしでアクセス違反が発生する理由も想定しています。これは、文字列にメモリが割り当てられていない場合 (S: PChar のみを宣言したことを思い出してください)、割り当てられていないためにアクセス違反が発生するためです。メモリにアクセスしました。

代わりに単に S: String を宣言すると、おそらくこれは発生しません。

拡張コメント後の追加:

PChar は、存在しなければならないデータ構造へのポインタにすぎません。また、PChar のもう 1 つの一般的な問題は、ローカル変数を宣言してから、その変数に PChar を他の Proc に渡すことです。これは、ルーチンが終了するとローカル変数が解放されますが、PChar は引き続きそれを指し、その後発生するためです。一度アクセスしたアクセス違反。

ドキュメントごとに存在する唯一の可能性はconst S: PChar = 'Hello, world!'、コンパイラが相対アドレスを解決できるため、これが機能するようなものを宣言することです。ただし、これは定数に対してのみ機能し、上記の例のような変数に対しては機能しません。上記の例のように行うには、PChar が指す文字列リテラルにストレージを割り当てる必要がありますS:String; P:PChar; S:='Hello, world!'; P:=PChar(S);

それでも String または Integer の宣言に失敗する場合は、変数がどこかで消えてしまうか、proc で突然表示されなくなる可能性がありますが、これは既に説明した既存の PChar の問題とは関係のない別の問題です。

最終的な結論:

それは可能ですが、コンパイラはそれを実行可能ファイルに保存されるS:PChar; S:='Hello, world!'ようにローカルまたはグローバル定数として単純に割り当て、次に実行可能ファイルに保存される別の定数を作成しますが、最後に割り当てられたもののみを指します、他のすべてはまだ実行可能ファイルにありますが、最後に割り当てられた場所のみを指しているため、正確な場所を知らなければアクセスできません。const S: PChar = 'Hello, world!'S := 'Hello'SS

したがって、どちらが最後のSポイントであったかによって、Hello, world!またはHello. 上記の例では、どれが最後であったかを推測することしかできず、おそらくコンパイラも推測することしかできず、最適化やその他の予測不可能な要因に応じて、時間が実行されるまでSに最後のものではなく、割り当てられていない Mem を突然指す可能性があります。Showmessage(S)アクセス違反。

于 2015-04-17T12:28:28.633 に答える
0

これがどこで問題を引き起こすかをどのように知ることができますか?

現時点で判断するのは難しいです。
Delphi XE2 での修正の性質を知っていれば、より適切な立場に立つことができます。
できることは、無名関数の使用を控えることだけです。
Delphi には手続き型の変数があるため、無名関数の準備はそれほど難しくありません。http://www.deltics.co.nz/blog/posts/48
を参照してください。

これが Delphi の以降のバージョンで修正されているかどうかを知りたいです。

@Sertac Akyuz によると、これは XE2 で修正されています。

個人的に私は匿名メソッドが嫌いで、コード ベースのかなりの部分が匿名 (イベント ハンドラー) になったため、Java プロジェクトで匿名メソッドを使用することを禁止しなければなりませんでした。
極端に控えめに使用するユースケースを見ることができます。
しかし、手続き型変数とネストされた手続きがある Delphi では...それほど多くはありません。

于 2013-10-06T14:11:24.520 に答える