我认为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]是如何访问嵌套方法的外部局部变量的方式。

最佳答案

没有看到整个程序的整体汇编代码(过程测试),仅假设您发布的代码片段是,在失败的代码片段上可能仅移动了一个指针,而在正确的版本上也移动了一些数据。

因此似乎S:= S或S:=''使编译器自己创建了一个引用,甚至可以分配一些内存,这将解释为什么它可以工作。

我还假设这就是为什么在没有S:= S或S:=''的情况下发生访问冲突的原因,因为如果没有为字符串分配内存(请记住您仅声明了S:PChar),则会由于未分配而引发访问冲突内存已访问。

如果仅声明S:String,则可能不会发生。

扩展注释后的添加:

PChar只是指向数据结构的指针,必须存在。 PChar的另一个常见问题是声明局部变量,然后将PChar传递给该变量给其他Procs,因为发生的情况是一旦例程结束,就释放了局部变量,但是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!'一样将其简单地分配为本地或全局常量,将其保存到Executable中,第二个S := 'Hello'然后创建另一个也保存到Executable中的,等等。最后一个分配的,所有其他分配仍在可执行文件中,但在不知道确切位置的情况下无法再访问,因为S仅指向最后分配的一个。

因此,取决于最后一个S指向哪个,即指向S还是Hello, world!。在上面的示例中,我只能猜测哪个是最后一个,谁知道编译器也只能猜测,并且取决于优化和其他不可预测的因素,Hello可能会突然指向未分配的Mem,而不是在执行S时指向最后一个Mem。然后引发访问冲突。

07-28 06:41
查看更多