您的位置:首页 > 编程语言 > Delphi

delphi if...else...语句和函数参数传递

2011-03-31 10:24 543 查看
1if语句

对if-then型语句,仅当条件满足时,语句才执行;对if-then-else型,if语句在两条语句中选择一条执行。条件用布尔表达式建立,句子中的条件部分可以是一系列条件(用and、 or 、 not等布尔操作符联接起来),if语句又可以嵌套另一个if语句,要注意的是,不能在第一句之后、else 关键词之前加分号,否则编译器将告知语法错误。

if ....then
begin
.......
with ..... do
begin
.......

end;

if .... then
begin
....

end
else
begin
.....

end ;//endIf

end//endBegin
else
begin
.......

end;

上例中绿色end可以加分号,而红色end不能加分号,即为“不能在第一句之后、else 关键词之前加分号,否则编译器将告知语法错误。”

2参数传递

这个帖子写得比较详细:http://www.pediy.com/bbshtml/BBS6/pediy6572.htm

关于Delphi中参数的传递和函数值的返回

前言:

高手们应该早知道了,不屑于写出来而已。真正的高手一个比一个潜的深,只剩下偶这样的小菜写些菜文给更小的菜。高手看时还请捂好大牙,多多指点。

不知各位小菜同胞对破解DELPHI程序有什么看法,反正我的感觉就一个字:怪。各位最先遇到的问题恐怕都是:我下了GetDlgItemInt、GetDlgItemText、GetWindowText....怎么什么也断不下来,甚至连Hmemcpy都不起作用?呵呵,从这里就能看出宝蓝的那批人成心想跟M$对着干,非搞出些新鲜的东东不可。

这回我们就来看看DLEPHI中对函数(过程)参数的传递是如何进行的。

我们知道WinAPI采用的调用约定是StdCall,也就是调用一个函数Func(arg1,agr2,agr3,arg4),你需要push arg4,push arg3,push arg2,push arg1,call Func 。在VC++里也是这种形式,所以一个函数有几个参数,可以非常直观地看出来。可是在DELPHI中就很奇怪了,在一个CALL前面你可能一个PUSH也看不到,怎么回事呢?听我慢慢道来。

DELPHI中的调用约定有StdCall,Cdecl,Safecall,Pascal和Register等几种方式,而DELPHI的默认方式是Register(为什么不是Pascal?)Register方式就是尽可能地使用寄存器来传递参数,减少堆栈的操作来提高速度。具体情况是怎样呢,看个例子先:

在FORM上放一个BUTTON,双击写代码如下:

代码:
function add1(a:Integer):Integer;    //一个参数
begin
add1:=a+a;
end;

function add2(a,b:Integer):Integer;    //两个参数
begin
add2:=a+b;
end;

function add3(a,b,c:Integer):Integer;    //三个参数
begin
add3:=a+b+c;
end;

function add4(a,b,c,d:Integer):Integer;    //四个参数
begin
add4:=a+b+c+d;
end;

function add5(a,b,c,d,e:Integer):Integer;  //五个参数
begin
add5:=a+b+c+d+e;
end;

function add6:Integer;        //加入一些局部变量
var local1,local2,local3,local4,local5:Integer;
begin
local1:=1;
local2:=2;
local3:=3;
local4:=4;
local5:=5;
add6:=local1+local2+local3+local4+local5;
end;

function add7(a,b,c,d,e:Integer):Integer;  //利用result来返回
begin
result:=a+b+c+d+e;
end;

function add8(a,b,c,d,e:Integer):Integer;StdCall;//StdCall调用方式
begin
add8:=a+b+c+d+e;
end;

procedure TForm1.Button1Click(Sender: TObject);
var a,b,c,d,e:Integer;
s1,s2,s3,s4,s5,s6,s7,s8,s:Integer;
begin
a:=1; b:=2; c:=3; d:=4; e:=5;
s1:=add1(a);
s2:=add2(a,b);
s3:=add3(a,b,c);
s4:=add4(a,b,c,d);
s5:=add5(a,b,c,d,e);
s6:=add6;
s7:=add7(a,b,c,d,e);
s8:=add8(a,b,c,d,e);
s:=s1+s2+s3+s4+s5+s6+s7+s8;      //必须要有这么几句
MessageDlg(IntToStr(s),mtConfirmation,[mbOK],0);  //不然编译器根本不去处理返回值
end;


用DEDE反一下看看,这个Button1Click的内容:

代码:
004403EC   55                     push    ebp
004403ED   8BEC                   mov     ebp, esp
004403EF   83C4D8                 add     esp, -$28    ;空出地方放局部变量
004403F2   53                     push    ebx
004403F3   56                     push    esi
004403F4   57                     push    edi
004403F5   33C9                   xor     ecx, ecx
004403F7   894DD8                 mov     [ebp-$28], ecx
004403FA   33C0                   xor     eax, eax
004403FC   55                     push    ebp

* Possible String Reference to: '関-?腽_^[嬪]?
|
004403FD   68E9044400             push    $004404E9

***** TRY
|
00440402   64FF30                 push    dword ptr fs:[eax]  ;这是DELPHI的例行公事
00440405   648920                 mov     fs:[eax], esp    ;据我观察只要调用VCL库的都要SEH
00440408   BB01000000             mov     ebx, $00000001  ;a:=1
0044040D   BE02000000             mov     esi, $00000002  ;b:=2
00440412   BF03000000             mov     edi, $00000003  ;c:=3
00440417   C745FC04000000         mov     dword ptr [ebp-$04], $00000004  ;d:=4
0044041E   C745F805000000         mov     dword ptr [ebp-$08], $00000005  ;e:=5

可以看出DELPHI的确不一样,把EBX,ESI,EDI能用的寄存器全都用上了,实在不行了才用[ebp-xx],
从下面的分析中也能看出这一点,DELPHI在能用寄存器时决不用堆栈。

00440425   8BC3                   mov     eax, ebx    ;这是add1的参数啦,不用PUSH的

* Reference to : TForm1.Proc_00440360()
|
00440427   E834FFFFFF             call    00440360    ;CALL add1

{
00440360   03C0                   add     eax, eax
00440362   C3                     ret      ;这样的确很快哟
}

0044042C   8945F4                 mov     [ebp-$0C], eax  ;s1:=add1(a)
0044042F   8BD6                   mov     edx, esi    ;add2的参数EDX=2
00440431   8BC3                   mov     eax, ebx    ;add2的参数EAX=1

* Reference to : TForm1.Proc_00440364()
|
00440433   E82CFFFFFF             call    00440364    ;CALL add2

{
00440364   03D0                   add     edx, eax
00440366   8BC2                   mov     eax, edx
00440368   C3                     ret
}

00440438   8945F0                 mov     [ebp-$10], eax  ;s2:=add2(a,b)
0044043B   8BCF                   mov     ecx, edi    ;add3的参数ECX=3
0044043D   8BD6                   mov     edx, esi    ;EDX=2
0044043F   8BC3                   mov     eax, ebx    ;EAX=1

* Reference to : TForm1.Proc_0044036C()
|
00440441   E826FFFFFF             call    0044036C    ;CALL add3

{
0044036C   03D0                   add     edx, eax
0044036E   03CA                   add     ecx, edx
00440370   8BC1                   mov     eax, ecx
00440372   C3                     ret
}

00440446   8945EC                 mov     [ebp-$14], eax  ;s3:=add3(a,b,c)
00440449   8B45FC                 mov     eax, [ebp-$04]  ;[EBP-4]=4
0044044C   50                     push    eax      ;终于看见PUSH了噢
0044044D   8BCF                   mov     ecx, edi    ;ECX=3
0044044F   8BD6                   mov     edx, esi    ;EDX=2
00440451   8BC3                   mov     eax, ebx    ;EAX=1

* Reference to : TForm1.Proc_00440374()
|
00440453   E81CFFFFFF             call    00440374    ;CALL add4

{
00440374   55                     push    ebp
00440375   8BEC                   mov     ebp, esp  ;这是C里面的方式啦
00440377   03D0                   add     edx, eax
00440379   03CA                   add     ecx, edx
0044037B   034D08                 add     ecx, [ebp+$08];[EBP+8]本来是第一个参数的
0044037E   8BC1                   mov     eax, ecx  ;这里[EBP+8]是第四个参数
00440380   5D                     pop     ebp
00440381   C20400                 ret     $0004
}

00440458   8945E8                 mov     [ebp-$18], eax  ;s4:=add4(a,b,c,d)
0044045B   8B45FC                 mov     eax, [ebp-$04]  ;[EBP-4]=4
0044045E   50                     push    eax      ;注意:先压进去的是第四个参数
0044045F   8B45F8                 mov     eax, [ebp-$08]  ;[EBP-8]=5
00440462   50                     push    eax      ;再压进第五个参数,Pascal从左至右
00440463   8BCF                   mov     ecx, edi    ;ECX=3
00440465   8BD6                   mov     edx, esi    ;EDX=2
00440467   8BC3                   mov     eax, ebx    ;EAX=1

* Reference to : TForm1.Proc_00440384()
|
00440469   E816FFFFFF             call    00440384    ;CALL add5(a,b,c,d,e)
0044046E   8945E4                 mov     [ebp-$1C], eax  ;s5=add5(a,b,c,d,e)

* Reference to : TForm1.Proc_00440398()
|
00440471   E822FFFFFF             call    00440398    ;add6 看看DLEPHI怎么处理局部变量

{
00440398   53                     push    ebx
00440399   56                     push    esi
0044039A   B801000000             mov     eax, $00000001
0044039F   BA02000000             mov     edx, $00000002
004403A4   B903000000             mov     ecx, $00000003
004403A9   BB04000000             mov     ebx, $00000004
004403AE   BE05000000             mov     esi, $00000005;哈哈,果然不出所料
004403B3   03D0                   add     edx, eax  ;它用上了一切能用的寄存器
004403B5   03CA                   add     ecx, edx  ;各位可以试试加上十来个局部变量
004403B7   03D9                   add     ebx, ecx  ;看它能坚持到几时
004403B9   03F3                   add     esi, ebx
004403BB   8BC6                   mov     eax, esi
004403BD   5E                     pop     esi
004403BE   5B                     pop     ebx
004403BF   C3                     ret
}

00440476   8945E0                 mov     [ebp-$20], eax
00440479   8B45FC                 mov     eax, [ebp-$04]
0044047C   50                     push    eax
0044047D   8B45F8                 mov     eax, [ebp-$08]
00440480   50                     push    eax
00440481   8BCF                   mov     ecx, edi
00440483   8BD6                   mov     edx, esi
00440485   8BC3                   mov     eax, ebx

* Reference to : TForm1.Proc_004403C0()        ;我想看看用result是不是有不同
|
00440487   E834FFFFFF             call    004403C0    ;其实和add5一样的,不写了
0044048C   8945DC                 mov     [ebp-$24], eax
0044048F   8B45F8                 mov     eax, [ebp-$08]
00440492   50                     push    eax      ;PUSH 5
00440493   8B45FC                 mov     eax, [ebp-$04]
00440496   50                     push    eax      ;PUSH 4
00440497   57                     push    edi      ;PUSH 3
00440498   56                     push    esi      ;PUSH 2
00440499   53                     push    ebx      ;PUSH 1

* Reference to : TForm1.Proc_004403D4()
|
0044049A   E835FFFFFF             call    004403D4  ;这个眼熟的吧,从右至左的StdCall方式

{
004403D4   55                     push    ebp
004403D5   8BEC                   mov     ebp, esp
004403D7   8B4508                 mov     eax, [ebp+$08]
004403DA   03450C                 add     eax, [ebp+$0C]
004403DD   034510                 add     eax, [ebp+$10]
004403E0   034514                 add     eax, [ebp+$14]
004403E3   034518                 add     eax, [ebp+$18]
004403E6   5D                     pop     ebp
004403E7   C21400                 ret     $0014    ;我还是觉得这样好看一些
}

* Reference to Form1
|
0044049F   8B5DF4                 mov     ebx, [ebp-$0C]
004404A2   035DF0                 add     ebx, [ebp-$10]
004404A5   035DEC                 add     ebx, [ebp-$14]
004404A8   035DE8                 add     ebx, [ebp-$18]
004404AB   035DE4                 add     ebx, [ebp-$1C]
004404AE   035DE0                 add     ebx, [ebp-$20]
004404B1   035DDC                 add     ebx, [ebp-$24]
004404B4   03D8                   add     ebx, eax    ;加起来


........下面的不写了,还值得一提的是在最后DELPHI总要弄出两个RET来,跳来跳去的,也算是DELPHI的特色吧。

上面讲的是自己定义的函数,要是用VCL库的东东,有时候更加莫名其妙一些。看例子:

建一个FORM,放一个BUTTON,一个EDIT,代码如下:

代码:
procedure TForm1.Button1Click(Sender: TObject);
begin
MessageDlg(edit1.text,mtConfirmation,[mbOK],0);
end;


呵呵太简单了是不是,用DEDE反下:(只写了关键部分)

代码:
004417BE   6A00                   push    $00    ;这是下面MessageDlg的第四个参数,找到没
004417C0   8D55FC                 lea     edx, [ebp-$04];??这是什么??

* Reference to control TForm1.Edit1 : TEdit
|
004417C3   8B83C8020000           mov     eax, [ebx+$02C8]  ;这是下面GetText的参数TControl吧
;看上面的Reference
* Reference to: controls.TControl.GetText(TControl):TCaption;
|
004417C9   E8D619FEFF             call    004231A4  ;得到EDIT的文本
004417CE   8B45FC                 mov     eax, [ebp-$04];这是参数一要显示的字串放入EAX
004417D1   668B0D00184400         mov     cx, word ptr [$00441800];这应该是参数二mtConfirmation
004417D8   B203                   mov     dl, $03  ;这是参数三[mbOK]

* Reference to: Dialogs.Proc_00441380
|
004417DA   E8A1FBFFFF             call    00441380  ;这个是MessageDlg
004417DF   33C0                   xor     eax, eax


如果按照上面的分析,看到GetText这里应该只有一个参数就是放入EAX的那个[ebx+02c8],从参考也可以看到这就是EDIT1,可是函数的返回值呢?刚执行完这个CALL后EAX中是没有的,mov eax,[ebp-04]后才出现了,返回值原来在[ebp-4]中。再向上找有一个莫名其妙的lea edx,[ebp-04],按照我上面的分析这应该表示GetText的第二个参数。可是GetText只有一个参数呀。


这种需要返回一个比较大的结构的函数,在VC中常用的方法是把一个指针当参数传递过去,而DLEPHI中我猜是不是做成一个隐藏的参数,像上面的GetText表面上看是返回一个TCaption,实际这个并不是放在EAX里返回来的。

总结一下:DELPHI对参数的传递是尽可能多地利用寄存器,一般第一个参数用EAX,第二个参数用EDX,第三个参数用ECX,多于三个参数的时候,对多出来的参数按照从左至右的PASCAL方式来压栈。
对于函数的返回值,有时尽管声明中说它的返回值是TCaption之类等,实际上并没有在EAX中返回,而是在保存一个隐藏的参数中,等需要时再复制过来。(这一点是猜想而已,如果哪位高人知道的话还请指点。反正我以前都是糊里糊涂地跟,结果出来就算了。其实仔细一分析还有点意思。)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: