procedure TForm1.Button1Click(Sender: TObject);
var
a, b, c: Integer;
xxx: procedure;
procedure AddAToC;
begin
Inc(c, a);
ShowMessage('AddAToC: Inc(c, a) = ' + IntToStr(c));
end;
procedure AddBToC;
begin
Inc(c, b);
ShowMessage('AddBToC: Inc(c, b) = ' + IntToStr(c));
end;
begin
a := 1;
b := 2;
c := 3;
xxx := @AddAToC;
xxx; //调用 AddAToC, 应该弹出 AddAToC: Inc(c, a) = 4
xxx := @AddBToC;
xxx; //调用 AddAToC, 应该弹出 AddAToC: Inc(c, a) = 5
ShowMessage(IntToStr(c));
AddAToC;
AddBToC;
ShowMessage(IntToStr(c));
end;
运行了一下,的确很奇怪
xxx;的时候应该是 AddAToC: Inc(c,a) = 4;
结果弹出来的却是:AddAToC:Inc(c,a) = 4566634;
在CPUwindow跟踪了一下,发现问题了。
Unit1.pas.45: a := 1;
0045E041 C745FC01000000 mov [ebp-$04],$00000001
Unit1.pas.46: b := 2;
0045E048 C745F402000000 mov [ebp-$0c],$00000002
Unit1.pas.47: c := 3;
0045E04F C745F803000000 mov [ebp-$08],$00000003
这里可以看到 1,2,3分别被放到[ebp-$04],[ebp-$0c],[ebp-$08]这些地方。
这个时候ebp=$12f608.
继续往下走,来到调用XXX的地方。
Unit1.pas.49: xxx := @AddAToC;
0045E056 B804DF4500 mov eax,$0045df04
Unit1.pas.50: xxx;
0045E05B 8BD8 mov ebx,eax
0045E05D FFD3 call ebx <-----这里开始调用xxx。
F7单步进去:
Unit1.pas.34: Inc(c, a);
0045DF19 8B4508 mov eax,[ebp+$08] <-----这一步ebp已经变化了,
0045DF1C 8B40FC mov eax,[eax-$04]
0045DF1F 8B5508 mov edx,[ebp+$08]
0045DF22 0142F8 add [edx-$08],eax
Unit1.pas.35: ShowMessage('AddAToC: Inc(c, a) = ' + IntToStr(c));
我的看法是,因为你直接调用的是函数指针,因此没能正常识别内层函数和外层函数在栈上
分配的局部变量。而正常调用因为有正常的保护,所以没有问题。
如果把a,b,c申明为全局变量,就正常了。