Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1332963
  • 博文数量: 436
  • 博客积分: 7854
  • 博客等级: 少将
  • 技术积分: 3225
  • 用 户 组: 普通用户
  • 注册时间: 2007-12-18 16:30
文章分类

全部博文(436)

文章存档

2013年(2)

2012年(56)

2011年(70)

2010年(308)

分类:

2010-08-05 15:23:22

概要介绍:
     Delphi是面向对象和事件驱动的,过去那种自上而下的结构化设计方法已不适
合在Delphi中编程, 不过Delph的事件句柄本身就是一个典型的过程,其次在编
程过程中您可能要接触
到大量的VCL和RTL中
的过程和函数。

     更重要的是,您一定会定义您自己的过程和函数。这就必须了解 Object
Pascal语言的过程和函数
的语法规则。

一:调用Delphi预定义好的过程和函数
     要调用Delphi预定义好的过程和函数很简单。对于VCL,当您把某个元件放到
FORM上后,Delphi将
把这个元件的DCU文件名加到您程序的USES部分,这意味着您可以调用这个元件的
所有方法(类的过程
和函数的统称)。对于RTL,当Delphi启动时已经自动把一些最常用到的DCU文件加
到程序的Uses部分,
这就是说您可以直接调用一些过程和函数,不过如果您需要调用其他的一些函数和
方法的话,你也许需
要自己把这些DCU单元加入到程序中。

     例如,如果在程序中需要调用外部应用程序,我们可能常用到一个API函数
ShellExecute,这个函
数是在ShellApi单元中声明的,因此,你必须在Uses部 分加入ShellApi单元。

二:过程的声明和定义
过程的声明是由过程首部和附属块组成的,其中过程首部的语法是:

procedure 标识符(参数列表)

附属块的语法如下:

调用约定;指示字

Procedure
     是保留字,过程标识符可以是任何合法的标识符,以后就是用这个标识符调用
这个过程。一个过程
可以有参数,如果有多个参数,参数之间用分号隔开,也可以没有参数,对于后者
,过程标识符后直接
跟分号。声明过程的程序示例如下:

Procedure MyProc(Param1,Param2:Integer;param3:Real);

声明了一个过程后,就应当在Implementation区定义这个过程,定义的规则是这样
的:

Procedure 过程标识符(形参); Begin {过程的执行体} End;

注意,过程还可以有自己的局部变量,程序示例如下:

     Procedure Myproc(Param1,Param2:Integer;param3:Real);
     Var I,j,K:integer;{这是三个局部变量}
     Begin
       I:=Param1;
       j:=Param2;
       k:=floattoint(Param3);
     End;

三:怎样调用过程 过程经声明和定义后,就可以在程序中调用,调用的形式是:


过程标识符(实参);

例如要调用上一节例子中的过程,程序示例如下:

MyProc(1,2,3);

注意:实参可以是常量、变量或表达式,但实参的个数和类型必须与形参完全匹配
,因此下列调用都是
非法的:

     Myproc(1,2);
     MyProc(1,2,3,4);
     MyProc(1.0,2.0,3.0);

但是,如果在声明过程的首部时,指定了参数的缺省值,则可以省略该参数。这里
有个必须条件是,如
果你省略了某个参数,则该参数后的所有参数(如果有)则必须都省略,也就是说
,你指定一个参数的
缺省值后,其后如果还有参数,也必须指定一个缺省值。例如:

{过程声明} procedure MyProc(param1:integer,param2:string='This is
test';param3:integer=0);

则在程序中可以这样调用过程:

     MyProc(1,'this is instance');
     Myproc(1);
     Myproc(1,'No Default',3);

当第一种调用时,参数Param3使用缺省值0,而第二种Param2和param3都使用缺省
值。第三种调用则使
用另外的参数值,这些调用方式都是允许的。

注意:在过程声明和定义中,多个参数是用分号隔开的,但在调用时是用逗号隔开
的。

在过程中还可以调用过程本身,这称之为递归。


四:函数的声明和定义
要定义和调用函数,首先要在程序的Type区声明一个函数,函数的声明跟过程的声
明有相似之处,不
同的是函数首部的语法:

Function   函数标识符(参数列表):返回值;

程序示例如下:

Function MyFunc(Param1,param2:Integer;param3:Real):Integer;

上述例子中声明了一个函数,函数名是MyFunc,有三个参数,前两个是整数类型,
第三个是实数类型,
返回类型是整数类型。

定义函数的程序示例如下:

function MyFunc(Param1,param2:Integer;Param3:Real):integer; Begin {函数
执行体} End;

注意:函数可以有参数,也可以没有参数,但必须指定返回类型,object
Pascal的函数可以返回任何数据类型除了文件类型,这一点跟Turbo Pascal 7.0
不同。

另外,还要注意,在过程的定义中,过程的执行体可以是空的,但函数的执行体至
少要有一个语句,那
就是必须返回一个数值。


五:怎样调用函数
调用函数跟调用过程有些相似,但也有不同。调用过程的语句是个独立的语句,我
们称之为过程语句,
例如您可以在程序中这么写:

      MyProc(1,2,3);

     而调用函数的语句不是一个独立的语句,因为它必须返回一个值,因此它通常
出现在一个赋值语句
的右边,如下例;

     ReturnValue:=MyFunc(1,2,3.0);

     函数调用也可以直接作为操作数,如下例:

     If MyFunc(1,2,3.0)>8   then...

     跟过程相同的是,调用函数时,实参的个数和类型必须与形参完全匹配,否则
调用将是非法的。


六:函数值是怎么返回的 Object
pascal语言的函数返回方法跟C语言的函数返回方法不同,在C语言中,返回值是通
过Return语句实现的, 而在object Pascal中,返回值是通过对函数
标识符赋值实现的,程序示例如下:


     Function MyFunc(Value:Integer):Integer;
     Begin
       ...
       MyFunc:=8;
     End;
在函数中,只要遇到对函数名赋值的语句,函数就返回,也就是说,对函数名的赋
值是函数执行的最后一
个语句。      


七:Result变量
Delphi自动在函数内部隐含了一个局部变量,这就是Result变量,这个变量的类型
跟函数的返回类型相同, 对Result的赋值相当于对函数名赋值,也就是说相
当于返回一个数值,程序示例
如下:

     Function MyFunc(Value:Integer):Integer;
     Begin
       ...
       Result:=8;
     End;    

如果Result变量仅仅是为了返回一个数值,那它也没有什么意义,Result变量的关
键在于它能出现在赋值
语句的右边,而如果函数名出现在赋值语句的右边,则意味着递归调用,另外对
Result变量赋值不一定就
是从函数返回,这一点跟对函数名赋值也不同,程序示例如下:


     Function MyFunc(X:integer):integer;
     Begin
       Result:=X;
       Result:=Result*X;
     end;



八:调用约定
所谓调用约定,是指参数的传递方式,Delphi共有四种传递方式,分别是
Register、Pascal、Cdecl、
StdCall。

指定调用约定的语法示例如下:

Procedure MyProc(Value:Integer);StdCall;

如果一个过程或函数没有指定调用约定,那就是采用缺省的Register方式。

Register方式 这个方式采用寄存器调用约定来传递参数,这是缺省的方式。

Pascal方式
这种方式采用Pascal调用约定来传递参数,也就是采用栈传递的方式,传递次序从
左到右,这种方式适用 于调用动态链接库(DLL)中的例程,而这些例
程是用其它语言编写的如C,C++。


Cdecl方式
这种方式采用C/C++的调用约定,参数从右到左依次传递到栈中。这种方式适用
于调用动态链接库中输
出的例程,这些例程一般是C/C++写的。

StdCall方式
这种方式采用Windows的标准调用约定来传递参数,传递次序从右到左,这种方式
适用于调用Windows的
API。


九:指示字 Assembler
     这个指示字表示过程或函数是用嵌入式汇编语言编写的。用了这个指示字后,
编译器对参数的处理作 了一些优化:
对于某些数值参数(关于数值参数的概念后面要讲到),如字符串类型的参数或者
字切长度不是1、2、4 的参数,编译器交 它们当作变量参数(关于变
量参数的概念后面要讲到)。
对于函数来说,没有隐含的Result变量,只有返回字符串的函数可以引用这个变量

对于参数或没有局部变量的过程或函数来说,编译器不生成栈框架,因为这需要耗
费时间。
因为函数必须返回一个,因此用汇编语言编写的函数也必须返回一个值,返回的规
则是这样的:
如果返回类型是有序的数据类型,AL返回一个8位数,AX能返回一个16位数,DX:
AX能返回一个32位数。 如果返回类型是实型,通过DB:BX:AX返回。
如果返回类型是指针,通过DX:AX返
回。
如果返回类型是字符中,通过@Result返回。

External 这个指示字适用于从DLL中导入过程或函数,表明过程或函数是外部的。


Forward
     一般情况下,当您调用一个过程或函数时,被调用的过程或函数必须是在
Type区已声明的,不过使用
Forward指示字可以让编译器向前查找过程或函数的 定义。程序示例如下:

Procedure MyProc(Param1,param2:integer);forward; procedure TForm1.
FormCreate(Sender:TObject);
Begin Myproc; End; Procedure Myproc; begin MessageBeep(0); end;

     在过程Myproc的定义中省略了参数,只写了过程名,这在使用Forward的情况
下是允许的。


十:过程或函数中变量的作用域问题
     毫无疑问,在程序的Type区声明的变量适用于整个程序,我们把它们称为全局
变量,当然它另外还有
Public和Private之分,这是另一个问题。

     在过程或函数的定义部分声明的变量只适用于这个过程或函数,我们把它称为
局部变量,注意:如果
过程或函数的参数是数值参数,这些参数也是这个过程或函数的局部变量。局部变
量只在它的作用域是有
意义,编译器不允许在作用域之外引用局部变量。

     局部变量被放在过程或函数的栈中,如果局部变量太多,要注意不要使栈溢出
。如果发生栈溢出,将 触发EStackOverflow异常。Object
Pascal提供了两个编译$MINSTACKSIZE和
$MAXSTACKSIZE,分别用于指
定最小和最大的栈空间。另外,栈溢出的检查总是进行的。

如果全局变量跟局部变量同名怎么办?Object Pascal规定,在过程或函数外,是
全局变量起作用,而在
过程或函数内部是局部变量起作用。当然我们建议尽量不要让全局变量跟局部变量
同名。


十一:参数类型 Object
     Pascal语言允许五种类型的参数(不是指参数的数据类型),分别是数值参数
、常量参数、变量参数、
无类型的参数、开放数组参数。

     不管是哪一种参数类型,在调用过程或函数时,实参跟形参在个数上必须相同
,在数据类型上必须赋
值相容。



------------------------------------------------------------------------
--------

     数值参数
     数值参数是一种最常见的参数,当调用过程或函数时,程序把实参的值复制一
个副本,传递给数值参 数,数值参数对过程或函数而言是局部变量,对数值
参数的修改不会直接改变实
参的值。
     注意:作为实参传递给数值参数的值不能是文件类型或包含文件类型的构造类
型。



------------------------------------------------------------------------
--------

     常量参数
     常量参数跟数值参数的区别在于,常量参数是只读的,也就是说不允许在过程
或函数中改变中改变常
量参数的值,对常量参数赋值是非法的,并且不允许把常量参数再作为实参传递给
另一个过程或函数。例
如下列程序中包含了不合法的代码:
     Procedure Myproc(Const Value:Integer);
     {参数名前加Const表示是常量参数}
     Begin
       Value:=3;{非法!因为不允许对常量参数赋值}
       AnotherProc(Value);{非法,常量参数不能再作为实参传递给另一个过程或
函数}
     End;



------------------------------------------------------------------------
--------

     变量参数 变量参数有点像C++的引用,参数在传递时传递的不是实参的副本
, 而是实参的地址, 这
样当参数在过程或函数中被改变时,实参的值也相应改变,在过程或函数中对
形参的引用实际上就是
对实参自身的引用。
     程序示例如下:
     procedure MyProc(Var Value:Integer);
     {参数名前加VAR表示是变量参数}
     Begin
        Value:=3;
     End;
     Procedure TForm1.Button1Click(Sender:Tobject);
     Var X:Integer;
     Begin
       X:=2
       Myproc(X);
     End;
     当形参是变量参数时,实参也必须是一个变量而不能是一个表达式,例如上例
中这样调用就是非法 的:MyProc(2);另外实参跟形参的数据类型必须完全
一致而不仅仅是赋值相容。

     当形参是变量参数时,实参可以是文件类型或包含文件类型的构造类型。
     当参数是构造类型时,使用变量参数具有优势,因为如果使用数值参数,在调
用时必须分配足够大
的内存空间来存放实参的副本,当参数比较庞大时,将花费大量的空间和时间,而
使用变量参数时,传
递的仅仅是实参的地址,其效率是显而易见的。



------------------------------------------------------------------------
--------

     无类型参数 所谓无类型参数,就是在过程或函数声明和定义时不指定参数的
数据类型,程序示例
如下:
     Procedure MyProc(Var Value);
     上例中,Value参数没有指定数据类型,但前面必须加Var或const,加Var表示
参数的值是可以改
变的,加Const表示参数是只读的。
     当参数是无类型参数时,实参可以是任意数据类型的变量或表达式,使用无类
型参数可以使程序
编制更加灵活,但也给编译器带来麻烦,因为它没法对 参数进行合法性检查。
     正因为参数是无类型的,因此在使用前最好先进行类型强制转换,把它转换成
某种特定的数据类
型,程序示例如下:
     Procedure Myproc(Var Value);
     Type
       TMyArray=arrayp[1..10] of Integer;
     Var I:integer;
     Begin
       for i:=1 to 10 Do TmyArray(Value)[i]:=i;
     End;

十二:过程或函数本身作为参数 在Object
Pascal中,允许把过程或函数本身作为参数来传递,因为过程或函数也
可以看作一种数据类型。

不能把标准的过程或函数作为参数,而只能把您自己定义的过程或函数作为参数传
递。


十三:嵌套和递归

所谓嵌套是指在过程或函数中又定义了过程或函数,程序示例如下:
     Procedure Myproc(Var Value);
     Type
       TMyArray=arrayp[1..10] of Integer;
     Var I:integer;
     Begin
       for i:=1 to 10 Do TmyArray(Value)[i]:=i;
     End;

上例中,在过程MyProc的内部又定义了SubProc过程,这就是嵌套。

您可以在一个过程或函数中定义几个平行的嵌套子程序,也可以在某个子程序中再
嵌套一层子程序,其
结构有点像磁盘上的目录,是一个树状结构。
     注意:外层子程序必须能完全套住内层子程序,部分套住是非法的。

下面的例子将说明使用嵌套要注意的事项,假设已经建立了如下的嵌套结构 :
     P0
       P1
        P11
        P12
          P121
      P2

     同层的子程序可以相互调用,例如P1和P2可以相互调用,P11和P12可以相互调
用(可能需要用到
forward指示字)。

     外层的子程序可以调用相邻的内层子程序,例如P0可以调用P1和P2,P1可以调
用P11和P12,但不能
隔层调用,如P0不能调用P12。

     内层的子程序可以调用外层的子程序,包括隔层调用,如P121可以调用P12,
也可以调用P1,也可以
调用P0。


------------------------------------------------------------------------
--------
   
     所谓递归就是在过程或函数中又调用了自己,这里面又分为两种类型,一种是
直接调用自己,称为
直接递归,一种在是在嵌套中间层之间的调用或内层调用外层,称为间接调用。

     递归的程序示例如下:

     Function Myfunc(I:Integer):integer;
     Begin
       If I=0 Then MyFunc:=0
       Else Myfunc:=MyFunc(I-1);
     End;   

     递归在编写一些数学计算程序时特别有用。

     使用递归最重要的是要注意设置递归结束条件,确保递归能够结构,否则将引
起死循环。

     过程或函数在每次调用自己时都要为局部变量和形参分配内存单元,当递归次
数比较多时, 可能要
耗费大量的内存空间。因此要平衡递归使用的利弊。
附录:写作花絮
      使用 Delphi 一直以来,都以为 Result 的作用就等于C/C++/C#中的 Return 一样,直接返回。最近在写一程序时,发现不
阅读(648) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~