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 的另一个常见问题是声明局部变量,然后将 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!'保存到可执行文件中一样,S := 'Hello'然后创建另一个也保存到可执行文件中的常量,依此类推 - 但S只指向最后一个分配的常量,所有其他人仍然在可执行文件中,但在不知道确切位置的情况下无法再访问,因为S只指向最后一个分配的位置。

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

于 2015-04-17T12:28:28.633 回答
0

我怎么知道这会在哪里引起问题?

目前很难说清楚。
如果我们知道 Delphi XE2 中修复的性质,我们会处于更好的位置。
您所能做的就是避免使用匿名函数。
Delphi 有过程变量,所以对匿名函数的需求并不那么可怕。
请参阅http://www.deltics.co.nz/blog/posts/48

我很想知道这是否在更高版本的 Delphi 中得到修复

根据@Sertac Akyuz 的说法,这已在 XE2 中得到修复。

就我个人而言,我不喜欢匿名方法,并且不得不禁止人们在我的 Java 项目中使用它们,因为我们的代码库中有相当一部分是匿名的(事件处理程序)。
在极端适度的情况下使用我可以看到用例。
但是在德尔福中,我们有过程变量和嵌套过程......没有那么多。

于 2013-10-06T14:11:24.520 回答