研究你永远不可能在实际中应用的东西是毫无意义的(至少在编程方面)——我只能这么说了。
to dirk兄:
var
ss:TEdit;
begin
TButton(ss).left:=0
//在此处设置断点,运行,点击Button1,然后Ctrl+Alt+L
TButton(ss).top:=0;
end;
——你看到了什么?—— ss
[csInheritable],false,false)
双击它,你就会看见:ss:TEdit ebx ——ebx!!!都是编译器的“优化”在起作用。
(还有: Name: Button1)
——这样的代码是绝对不符合最基本的编程原则(注意,不是“规范”)的,您会在编译
时得到一行警告: Variable "ss" might not have been initialized.
——一个合格的程序应该没有任何警告。再说,应该没有人会认为这种用法有实用价值。
另:
如果我们把Project - Options - Compiler - Optimization 选项关闭,再次编译运行
的话,再上述的断点处按下Ctrl+Alt+L,就会看到: ss
[],false,fase), 双击之,可以
看到:ss: TEdit $12F5DC ——这下未初始化的对象终于“现形”了!接着单步运行之,
我们就会很容易的得到 EAccessViolation 错误。
——没有这个“尚方宝剑”,我还真是说服不了一些同道,看来还要努力呀!
对于任何编程方面的疑问,都应该从原理上加以透彻的了知,而不是在看到了表面现象
之后就以为自己“知道了”。当我们无法从一个层面上解释某种现象的时候,就应该深入
这个现象之后,从更加深层的机制进行突破。——一切现象都是有原因的,编程语言也是
一样的。
附上汇编代码:
var //打开编译优化 watch: TControl(eax) TControl(ebx) ss
ss:TEdit;
begin //1.push ebx Form1 Button1 ebx(Button1)
//2.xor edx,edx 为SetLeft过程准备参数
//3.mov eax,ebx 将ss送至eax——编译器认为ss已经被置于ebx
Button1 Button1 Button1
ss.Left:=0
//4.Call TControl.SetLeft 调用TControl(ss).SetLeft(0)过程
//实际上就是调用 TControl(Button1).Setleft(0)
ss.Top:=0
//...
end;
在上面的begin处设置断点,就会发现此时的ebx,edx均指向Button1。edx指向Button1可以
理解——它就是Sender。那么ebx呢?我认为是Delphi在前面的procedure TControl.Click;
过程的遗留问题(没办法跟进去,只能“认为”了,555...)。如果我们在ss.Left之前加上
一句“ss:=nil;”,再次查看汇编代码,我们就会发现Delphi只是1、2句之间插入了一句
“xor ebx,ebx”——显然,在打开编译优化的情况下,Delphi认为局部变量ss是被存放在
寄存器ebx中的,编译器不会主动为它赋值或清零,除非你用代码进行初始化。因此,在dirk
的代码中,ebx的内容就是Button1。如果我们在begin和ss.Left之前加入一个比较复杂的
运算(例如:Form1.Caption:='123'+IntToStr(Tag);),那么ebx就会被改变成一个很难
把握的值,经过这个运算,ebx和Button1的关系自然就被打破了——“没有Create的对象
其实就是Sender”这个现象的原因找到了!