我遇到了“由于优化,变量x在此处无法访问”的问题。

11

即使构建配置设置为“Debug”,并且优化为False,我仍然会收到“Variable ForAllUsers inaccessible here due to optimization”的错误。因此,我无法调试我的程序。

我为什么会得到这个错误消息?
按下运行按钮时运行哪个构建?
我该如何查看?


procedure Test(ForAllUsers: boolean);
VAR
   FName, Path1, Path2: string;
   RootKey: HKEY;
begin
 Result:= FALSE;
 TRY
  if ForAllUsers
  then
    begin
     RootKey:= HKEY_CLASSES_ROOT;
     Path1:= '';
     Path2:= '';
    end
  else
    begin
     RootKey:= HKEY_CURRENT_USER;           <----- Break point here
     Path1:= '\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\';
     Path2:= '\Software\Classes\';    
    end;

更新:
距离我发布这个问题仅几分钟,它就已经被投票两次并被加星两次了。看起来这是一个相当普遍的问题。


展示你遇到问题的代码,我会提供帮助。 - Toby Allen
嗨,Andreas。我知道D7的问题。它很烦人,但实际上并不是那么严重。但这不是关键。我正在进行我的程序。 - Gabriel
我认为这是内置调试器优化的一种。我只知道你正在尝试观察调试器视为优化的本地变量的最后一个(或唯一一个)出现,因此无法从中获取值。我只知道一种解决方法-在方法中再次使用此变量并在最后一次使用之前捕获它。 - user532231
@daemon_x - 但我的编译器没有设置为优化代码。优化为FALSE。在D7中,false表示false = 不优化/更改代码。 - Gabriel
@Altar - 我知道,这对我来说看起来像是一个长期的问题,因为我有D2009,它也会出现相同的情况(包括Optimization = FALSE)。 - user532231
显示剩余5条评论
3个回答

12
我们都有时会遇到这种情况。我有时会在需要调试引用该变量的变量的位置添加一些无关代码,仅仅是为了调试。例如:
if x>0 then x := x*1;

或者,如果它是布尔值:

if b then b := not not b;

通常这样的代码就足以让编译器生成将变量保持在内存中,以便调试器能够检查它的代码。确保将代码放在程序末尾! 在提交代码之前记得删除这些代码。


1
+1 对于 if b then b := not not b; :) 当然你是对的。无论如何,只读取值就足够了;你不需要赋值。 - user532231
嗨,David。在 Delphi 7 下,你有遇到过这种情况吗?我之前从未见过(除非执行点确实在例程的末尾)。 - Gabriel
@daemon_x 我永远无法确定你需要做多少工作才能说服编译器按照我的意愿行事。我倾向于采取保守的方式! - David Heffernan
@Altar 我从来没有使用过 Delphi 7。我直接从6升级到了2010。 - David Heffernan
1
@David,不错的解决方法,但我不认为这是答案,也不认为XE有这个bug。Delphi 2010没有AFAICT。 - Johan
@Johan 在我发布这篇文章之前还没有代码。由于我们不知道Altar使用的精确编译器设置,所以很难确切地知道发生了什么。 - David Heffernan

9

让我们看一下代码优化前后的区别:

procedure test;
var
  x,y,z: integer;
begin
  x:= 1;   //x is stored in register EAX.
  Inc(x);  
  y:= x;   //this is a no-op because it's just a rename.
//After this point x is no longer used.
//Here you will get `Variable x inaccessible here due to optimization`
  z:= 0;   //z is never used 
  if (y = 1) then Inc(z);  //because Delphi knows this code will never execute
end;

这是经过优化的汇编代码:

Project5.dpr.12: x:= 1;   //x is stored in register EAX.
004085E8 B801000000       mov eax,$00000001
Project5.dpr.13: Inc(x);  
004085ED 40               inc eax
Project5.dpr.18: if (y = 1) then Inc(z);
004085EE 48               dec eax  //test to see if eax=1, triggers `jz` if true.
                                   //Delphi put it in to facilitate the `if`, but
                                   //is not smart enough to eliminate it :-)
Project5.dpr.19: end;
004085EF C3               ret 

以下是未经优化的代码:

Project5.dpr.11: begin    //note that Delphi doesn't use registers, but the stack 
                          //to keep variables.
004085E8 55               push ebp
004085E9 8BEC             mov ebp,esp      //init the stack frame.
004085EB 83C4F4           add esp,-$0c
Project5.dpr.12: x:= 1;   //x is stored near the top of the stack.
004085EE C745FC01000000   mov [ebp-$04],$00000001
Project5.dpr.13: Inc(x);  
004085F5 FF45FC           inc dword ptr [ebp-$04]
Project5.dpr.14: y:= x;   //y sits on the stack frame.
004085F8 8B45FC           mov eax,[ebp-$04]
004085FB 8945F8           mov [ebp-$08],eax
Project5.dpr.17: z:= 0;    //z is also in the stack frame.
004085FE 33C0             xor eax,eax
00408600 8945F4           mov [ebp-$0c],eax
Project5.dpr.18: if (y = 1) then Inc(z);
00408603 837DF801         cmp dword ptr [ebp-$08],$01
00408607 7503             jnz $0040860c
00408609 FF45F4           inc dword ptr [ebp-$0c]
Project5.dpr.19: end;     //all vars stay in scope.
0040860C 8BE5             mov esp,ebp  //until the stack frame is dismantled.
0040860E 5D               pop ebp  
0040860F C3               ret 

如果关闭优化,您的情况应该永远不会发生,但是...

您也可以在源代码中设置优化开/关:

{$Optimization on/off}  or
{$O+/-}

如果这行代码在你的例程前面,它将覆盖全局设置。 http://docwiki.embarcadero.com/RADStudio/zh-cn/%E4%BC%98%E5%8C%96_(Delphi)

嗨,约翰。这是我正在做的事情!!!我已经在“项目选项”中将优化设置为OFF。我不明白在PAS文件中再次将其设置为OFF的意义所在。特别是当我想从Debug切换到Release构建时,那将是一个真正的动物园。可能我会忘记删除{$O-}指令。但我非常确定这只是Delphi XE的一个bug。总之,他们只发布了Delphi XE的更新1。对的。我们将不得不等待更新2、3、4、5……甚至6。 - Gabriel
我只是在说你可能在某个地方留下了一个 {$O+}。此外,您可以在变量超出范围之前断点,并右键单击以获取菜单,选择“调试->查看CPU”。将相关部分复制粘贴到问题中。 - Johan
我只是在说你可能在某个地方留下了一个{$O+}。这就是我的观点:我不使用这个编译器指令,因为我可能会忘记它。这是我的规则。我会搜索它,但这将是无用的,因为我从未违反过这个规则。 - Gabriel
1
@Altar,1. Delphi生成的汇编代码是什么样子的?2. 尝试加入{$IFDEF DEBUG} {$O-} {$ENDIF},现在Delphi生成的代码是什么?3. 没有{$IFDEF}的情况下与(2)相同。 - Johan

2
您发布的代码“原样编译不了”,所以我不能百分之百确定我的个人修改并运行它是否破坏了复制的案例……但我无法重现您特定的问题。还有其他人可以吗?
当优化开启时,调试器/评估器会抱怨,但关闭优化并重新构建后,问题肯定会消失。您确定已经进行了适当的重建吗?
我有点不同意David的说法,“我们都时不时地遭受这种问题”。除了已知和可预测的边界情况(变量在结束断点时超出作用域),我实际上从来没有遇到过这个问题。只要我防止我的同事将dproj与优化一起检入版本控制,就可以了。

1
你说得对,也许是我做错了什么。我从来没有真正努力去理解是什么原因导致我使用像我在答案中描述的那样的技巧。我通常更感兴趣的是解决手头的问题,而不是深入了解内部机制。 - David Heffernan
@David 这可能比像我一样过度关注这些次要的生产力问题更健康的态度...不过,当我们的初级程序员第一次遇到这些问题时,他们往往会询问这些事情,而我不想回答他们“忽略和摆弄直到它工作”。除非有紧急情况。 :) - Paul-Jan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接