我认为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。然后引发访问冲突。